text:
How many bytes is it
This time, we want render the count of bytes in a human readable way to user. Let's write a program (a full one or a function or other acceptable formats) to do this.
Input
An non-negative integer, in range \$0\leq n<2^{31}\$, which means n bytes.
Output
A string, the human friendly representation of n bytes.
Convention

If n is less than 1000, add B after n and output; otherwise...
Convert to a number less than 1000, plus one of these units: KiB, MiB, GiB; round the number to 3 figures
For more details of conversion, check out these testcases

Testcases
0 -> 0B
1 -> 1B
42 -> 42B
999 -> 999B
1000 -> 0.98KiB
1024 -> 1.00KiB
2018 -> 1.97KiB
10086 -> 9.85KiB
100010 -> 97.7KiB
456789 -> 446KiB
20080705 -> 19.2MiB
954437177 -> 910MiB
1084587701 -> 1.01GiB
1207959551 -> 1.12GiB
2147483647 -> 2.00GiB

Rules

This is code-golf, so shortest bytes win
Standard loopholes forbidden
You should output exactly the same to the testcase:

No white space or other symbols between number and units;
Use KiB, not KB, kib, or kb;
Leading or trailing white spaces are optional

@tsh "Use KiB, not KB" - are you sure about the 1000 then? The SI units are 1kB=1000B and 1KiB=1024B.

@ngn Yes. 1000 should be converted to 0.98KiB, not 1000B, or 1kB. And there are always at most 3 significant figures, not 4 (for 1000B).

I think 0.98KiB is using 2 significant figures, not 3 – the leading 0 doesn't count.

@O.O.Balance Ah, you are right. I would change the description some how (testcases would be unchanged); So 3 significant figures now.

Why not leave it significant figures and change the one testcase? Significant figures is a standard way of formatting numbers (and I already have a working program :P).

@user202729 Because there isn't 0.08bits.

@user202729 The 3 figures rule seems to apply only if $n\ge1000$.

How about 1023079, should the output be 999KiB or 0.98MiB?

(How exactly should the numbers be rounded? Half-up?)

Besides 1023079, another interesting test case is 1023488.

I remember seeing this question in the Sandbox, though I don't know how long it was there - either it should have been there longer, or more people need to visit the Sandbox and leave comments there.

So, the rule of the 3 digits does not apply for n < 1000, does it?

@user202729 "3 figures" rule only apply to n >= 1000.

@PieCot "3 figures" rule only apply to n >= 1000.

@YiminRong I didn't even noticed such a testcase. Since it's too late to change anything. I would say both are OK.

@sundar Obviously the latter.

JavaScript (ES6), 78 74 bytes

f=(n,i)=>n<999.5?i?n.toPrecision(n<1^3)+' KMG'[i]+'iB':n+'B':f(n/1024,-~i)
<input oninput=o.textContent=f(this.value)><pre id=o>

Edit: Saved 4 bytes thanks to @Arnauld.

1023079 outputs 0.98MiB should probably be 999KiB.

@YiminRong Fixed for n up to 1023487, then rolls over for 1023488.

Python3,  90 bytes
(76 70 bytes 3-most-significant-digits version)
h=lambda v,u=0:v>999.4and h(v/1024,u+2)or"%.*f%sB"%(u and(v<10)+(v<100),v,"KiMiGi"[u-2:u])

Version whose outputs are strictly adherent to test cases and author's suggestions.
list(map(lambda v: print("{} --> {}".format(v, h(v))), (0, 1, 42, 999, 1000, 124, 2018, 10086, 100010, 456789, 20080705, 954437177, 1084587701, 1207959551, 2147483647, 1023079)))

0 --> 0B
1 --> 1B
42 --> 42B
999 --> 999B
1000 --> 0.98KiB
1024 --> 1.00KiB
2018 --> 1.97KiB
10086 --> 9.85KiB
100010 --> 97.7KiB
456789 --> 446KiB
20080705 --> 19.2MiB
954437177 --> 910MiB
1084587701 --> 1.01GiB
1207959551 --> 1.12GiB
2147483647 --> 2.00GiB
1023079 --> 999KiB
1023488 --> 0.98MiB
1043333 --> 0.99MiB
1043334 --> 1.00MiB

Try it online!
Here, a version that produces the most 3 significant digits for the test cases.
h=lambda v,u=0:v<1e3and("%.3G%sB"%(v,"KiMiGi"[u-2:u]))or h(v/1024,u+2)

The original test cases:
list(map(lambda v: print("{} --> {}".format(v, h(v))), (0, 1, 42, 999, 1000, 124, 2018, 10086, 100010, 456789, 20080705, 954437177, 1084587701, 1207959551, 2147483647)))

0 --> 0B
1 --> 1B
42 --> 42B
999 --> 999B
1000 --> 0.977KiB
124 --> 124B
2018 --> 1.97KiB
10086 --> 9.85KiB
100010 --> 97.7KiB
456789 --> 446KiB
20080705 --> 19.2MiB
954437177 --> 910MiB
1084587701 --> 1.01GiB
1207959551 --> 1.12GiB
2147483647 --> 2GiB

Try it online!

Sorry, I should have been more specific. You are outputting ’2GiB’ instead of ’2.00GiB’.

1023488 outputs 1e+03KiB, should probably be 0.98MiB.

@O.O.Balance, fixed ;). Thanks to point it out.

@YiminRong I've updated the code to be adherent to the post author's guidelines

1023488 --> 1000KiB

@O.O.Balance, fixed and added some extra corner cases.

C (gcc), 108 102 bytes
Works as Yimin Rong suggests.
f(float i){char*a="GiMiKi"+6;while(i>=999.5)i/=1024,a-=2;printf("%.*f%.2sB",*a&i<100?i<10?2:1:0,i,a);}

Try it online!
Unglofed

f(float i){
    char*a="GiMiKi"+6;
    while(i>=999.5)
        i/=1024,a-=2;
    printf("%.*f%.2sB",*a&i<100?i<10?2:1:0,i,a);
}

C (gcc), 108 101 99 bytes
Works as O.O.Balance suggests.
f(float i){char*a="GiMiKi"+6;while(i>999)i/=1024,a-=2;printf("%.*f%.2sB",*a&i<100?i<10?2:1:0,i,a);}

Try it online!
Unglofed

f(float i){
    char*a="GiMiKi"+6;
    while(i>999)
        i/=1024,a-=2;
    printf("%.*f%.2sB",*a&i<100?i<10?2:1:0,i,a);
}

f(1023079) outputs 0.98MiB, output should probably be 999KiB.

@YiminRong Reading the question, I tend to disagree; I think this should be treated as an extension of the ’1000’ case.

You should remove the ’include’ from the header or add it to the byte count. It works fine without it though.

f(1023488) outputs 1000KiB for the first one, output should probably be 0.98MiB. Using >=999.5 in the while loop fixes it.

Java 8, 130 112 108 bytes
n->n<1e3?n+"B":"".format("%.3G"+(n<1e9?n<1e6?"K":"M":"G")+"iB",n<1e9?n<1e6?n/1024.:n/1048576.:n/1073741824.)

Uses 3 significant figures, as per the original challenge. Try it online here.
Thanks to Jakob for golfing 4 bytes.
Ungolfed:
n -> // lambda
n < 1e3 // special case: number of bytes is < 1000
    ? n + "B" // output the number of bytes
    : "".format("%.3G" // otherwise output 3 significant digits
    + (n < 1e9 // if it's less than 1 billion bytes
        ? n < 1e6 // and it's less than a million bytes
            ? "K" // output kibibytes
            : "M" // else output mebibytes
        : "G") // else output gibibytes
        + "iB", // common suffix
        n < 1e9 // if it's less than 1 billion bytes
            ? n < 1e6 // if it's less than 1 million bytes
                ? n/1024.     // convert to kibibytes
                : n/1048576.  // else convert to mebibytes
            : n/1073741824. ) // else convert to gibibytes

You can replace String.format with "".format.

@Jakob Thanks, indeed I can. I always forget that you can invoke static methods like that.

APL (Dyalog Classic), 60 bytes
{'BKMGi'[i,4 0/⍨×i],⍨v⍕⍨(3-≢⍕⌊v←⍵÷i⊃p)××i←⍵+.≥1e3×p←1024*⍳4}

Try it online!

Using 1023488 outputs 1000KiB, should probably be 0.98MiB.

@YiminRong the challenge says "Convert to a number less than 1000, plus one of these units: KiB, MiB, GiB; round the number to 3 figures" - that is precisely what I've done: the number is 1023488/1024 = 999.5 < 1000, the unit is KiB, when rounded the number becomes 1000, which does have 3 significant figures (the leading 1,0,0) and the rest are 0 (the trailing 0).

Rust, 275 bytes
fn h(n:u32)->String{let i=[(30,"Gi"),(20,"Mi"),(10,"Ki"),(0,"")].iter().map(|x| (n as f32/2f32.powi(x.0),x.1)).filter(|y| y.0<999.5).last().unwrap();format!("{}{}B",if n<1000{format!("{}",n)}else{format!("{:.4}",format!("{:.2}",i.0)).trim_right_matches(".").to_string()},i.1)}

The basic idea is borrowed from the other answers, but formatting the string to have the proper number of significant digits, not have trailing decimal points, and not have numbers like 423.78 takes over half of the code... and it still rounds wrong (19.1 should be 19.2)

A TIO link with some test cases would be nice. I always like to run the code :D

https://bit.ly/2J7wseA

Stax, 34 bytes
ì▌╚ƒ░¿n↕─.êN┴▀m▌@àΔ*¡I⌠'}¿Äüz♥∞└6]

Run and debug it
Writing this, I ran into a bug in stax's log10 operation.  Log10 of 1000 yields 2.9999...., which is particularly unhelpful for this challenge.  I ended up using string length instead. Unpacked, ungolfed, and commented, it looks like this.
Vk<             is input less than 1000?
{               start block one
  yp'BP         print input followed by "B"
}               end block one
{               start block two
  x             push input as number
  y%v3/X        push (len(input) - 1) / 3, and store in the X register
  A*|2          multiply by 10, then get calculate two-power
  :_2j          floating point division, rounded to 2 places as a string
  p             print without trailing newline
  "GKM"x@.iB+P  use x register to index into "GKM" string, then concat "ib" and print
}               end block two
?               if-else based on initial condition

Run this one

Python 3, 87 81 80 bytes
def f(n):
 for u in'','Ki','Mi','Gi':
  if n<1e3:return"%.3G%sB"%(n,u)
  n/=1024

Try it online!
This does not work, I will need to think about how to fix it.
-6 bytes from Jo King
-1 bytes from Stewie Griffin

@JoKing Oh. I knew I could do in[], but didn't think I could in''

Note that it doesn't match the format exactly, leaving out zeros after the decimal point.

@O.O.Balance Does it have to match exactly? 0B, 1B, 42B are not 3 sig figs. Also to get pedantic it should be 910.MiB.

Well, it says: "You should output exactly the same to the testcase"

1023488 outputs 1E+03KiB, should probably be 0.98MiB.

C, 154 165 191 189 bytes (full program)
Big thanks to O.O.Balance and l4m2 for suggestions and providing this TIO and this TIO!
double atof(),x;main(i,v)char**v;{x=atof(*++v);if(x<1e3)printf("%sB",*v);else{for(;x>=999.5;++i)x/=1024;printf("%.*f%ciB",2-(int)log10(x),x,"KMG"[i-3]);}}

Save as doit.c, compile using gcc -o doit doit.c -lm, and run:
> ./doit 1000
0.98KiB

Test cases:
> for i in 0 1 42 999 1000 1024 2018 10086 100010 456789 1023079 1023488 20080705 954437177 1084587701 1207959551 2147483647 ; do ./doit $i ; echo; done
0B
1B
42B
999B
0.98KiB
1.00KiB
1.97KiB
9.85KiB
97.7KiB
446KiB
999KiB
0.98MiB
19.2MiB
910MiB
1.01GiB
1.12GiB
2.00GiB

You can replace the while loop with for and get rid of braces; use K&R style arguments for main, get rid of puts("B"); by duplicating it in both branches. Also, it works fine for me without the second function declaration (remove the typedef as well). => 165 bytes: https://tio.run/##PctBC4IwAAXgvxIDYZvTNquDLBO6dIh@QXRYa7PBdKEmA/Gvt0Si43vvezKppAzh4d53q1aidxrKp2gx4rUwDTRkQEvGAx9/yBcLw3E8IG409HumNujVmqbXEETdERA8L8p2atSuhdwfijzP0x2PY4P8umA02/K/T7GOpJlPWQLnCllXMQo9Ip6AsizPlxO4mhvi0xRCYJTSj9RWVF1IbP0F

@O.O.Balance -- Wow! I can never compete with the pros at golfing C!

float atof();main(i,v)char**v;{double x=atof(*++v);if(x<1e3)printf("%sB",*v);else{for(;x>=999.5;++i)x/=1024;printf("%.*f%ciB",2-(int)log10(x),x,"???KMG"[i]);}}?

@l4m2 This gives output 1000B for input 1000. See here: https://tio.run/##PYvNCoIwAIBfRQbB/rTN6iDLhC4doieIDmttNpgu1GQgvnpLOnT9flRaKxWjcV4OiRy8gUg00rbQ0hGpp@wwHsX08O@700kofwUmZETCGhj2XG/Qq7PtYCBY9UdA8WK06/VkfAdFOJRFUWQ7QYhFYV1ylm/Fv8@wWSm7THkKF4ScrzmDAdFAQVVV58sJXO0NiXmOMXLG2EcZJ@s@pq75Ag

@O.O.Balance Still use double fine

[C (gcc)], 155 bytes
double atof(),x;main(i,v)char**v;{x=atof(*++v);if(x<1e3)printf("%sB",*v);else{for(;x>=999.5;++i)x/=1024;printf("%.*f%ciB",2-(int)log10(x),x,"???KMG"[i]);}}
Try it online!

"???KMG"[i] => "KMG"[i-3]?

C (gcc), 135 bytes
e;double atof(),x;main(i,v)int**v;{for(x=atof(*++v);x>=999.5;e++)x/=1024;printf(e?"%.*f%ciB":"%.*fB",e?2-(int)log10(x):0,x,".KMG"[e]);}

Try it online!
Quite change from Yimin Rong's solution


text:
Calculate the sum of all odd digits of pi in range
Introduction
In the not so distant future with the AI revolution, we will need a way to solve their captchas to prove we're not humans.
This challenge was inspired by CommitStrip.
Challenge
One such captcha is the challenge of calculating the sum of all odd digits of Pi from digit 1 to digit n, including the first digit, being 3.
Output

The calculated sum of all odd digits in the range 1 to n.

Rules

All the digits of pi must be calculated by your program at runtime, how you implement that is up to you.
Hard-coded constants of pi are not allowed.
This is code-golf, so shortest bytes win.

Example
If n = 6, we must find the sum of the odd digits in [3, 1, 4, 1, 5, 9] meaning an output of 19
Other
Since this questions keeps getting marked as a duplicate of this, allow me to explain how this is different.
This question asks for the sum of a range of numbers, not finding the nth decimal.

Does the range include the first 3? Does the count start at that three (is it the first element or is it seperate from the count, i.e, the zeroth element)?

@Mackan90096 Even though you've specified the output "Must be Calculated", this requirement is Non-observable, meaning it's difficult and case-by-case to determine if an answer is valid. It would be easier to define your challenge in such as way that hard-coding the answer isn't a good tactic, like having variable input.

Edited to make more sense by making the range 1 to n, @ATaco

If n = 6, we must find the sum of the odd digits in {3, 1, 4, 1, 5, 9}, so 19? If this is what you mean, then in the case n=31,415 the sum equals 78,664, not 78,659 as you stated in the example. Please clarify.

Also, I meant the three at the start, the first digit, not the first three digits like you wrote.

My mistake, I've gone ahead and fixed it, @sagiksp, and yes, including the first digit.

All the digits of pi must be calculated by your program: does that mean no hardcoding of the first N digits of PI or no built-in at all? (Edit: apparently, this rule comes from the initial version of the challenge, where the number of digits was fixed to 31415. Now that the length is taken as input, it should probably be removed.)

How about žs in 05AB1E? Since the digits are technically hardcoded in the language source code, does that mean it's not allowed?

"All the digits of pi must be calculated by your program" is a non-observable requirement, so I'd recommend removing it.

The title is misleading. The sum of all digits of pi is infinity

The question suffers from unclear working. I think the author meant to imply no built-ins for π, i.e. you have to code the algorithm yourself. If this is the case, the question indicated as a duplicate is actually different, because built-ins are allowed in that question.

As far as I know, the only way this challenge can be done is by computing all digits of π up to n'th digit. Also as far as I know, all the existing algorithms to compute π at n'th digit in decimal also compute π at all digits before position n. Therefore programs in the other challenge can be trivially ported into this one. I agree that it's a duplicate.

This is the guideline to close as duplicate. In my opinion, computing π is much harder than filter out odd digits and take the sum, so (2) is true. (1) and (3) is obviously true.

Husk, 7 bytes
As you can see here1 the following program, does indeed compute all the digits itself:
Σf%2↑İπ

Try it online!
Explanation
Σf%2↑İπ  -- implicit input N, eg: 8
     İπ  -- compute all digits of pi: [3,1,4,1,5,9,2,6,5,3,...
    ↑    -- take N: [3,1,4,1,5,9,2,6]
 f%2     -- filter out even ones: [3,1,1,5,9]
Σ        -- sum: 19

1: This is a link to the Haskell code which is generated by Husk, in case you're lost (it's quite long) just search for func_intseq 'π'..

Python3, 346 bytes
Based on a recursive implementation of the Chudnovsky algorithm, one of the fastest algorithms to estimate pi. For each iteration, roughly 14 digits are estimated (take a look here for further details).
def f(n):import decimal as d;d.getcontext().prec=n+10;q=lambda n,k=6,m=1,l=13591409,x=1,i=1:(d.Decimal(((k**3-16*k)*m//i**3)*(l+545140134))/(x*-262537412640768000)+q(n,k+12,(k**3-16*k)*m//i**3,l+545140134,x*-262537412640768000,i+1)if i<n else 13591409);return sum([ord(i)%2 and int(i)for i in str(426880*d.Decimal(10005).sqrt()/q(n//14+1))[:n+1]])

Ungolfed version
def f(n):
    import decimal as d
    d.getcontext().prec = n + 10
    q=lambda n, k=6, m=1, l=13591409, x=1, i=1:\
        (d.Decimal(((k ** 3 - 16 * k) * m // i**3) * (l + 545140134)) / (x * -262537412640768000) +
         q(n, k+12, (k ** 3 - 16 * k) * m // i**3, l + 545140134, x * -262537412640768000, i+1)if i < n else 13591409)
    return sum([ord(i) % 2 and int(i)for i in
                str(426880 * d.Decimal(10005).sqrt() / q(n // 14+1))[:n + 1]])

Function q computes the value of S(see the reference), than it is used to divide the term 426880 * d.Decimal(10005).sqrt() and get the value of the approximation of pi.
The precision is controlled by setting the value of d.getcontext().prec.
A a little bit longer (372 bytes), more golfed but all-in-one version:
f=lambda n,k=6,m=1,l=13591409,x=1,i=0:not i and(exec('global d;import decimal as d;d.getcontext().prec=%d+10'%n)or sum([ord(i)%2 and int(i)for i in str(426880*d.Decimal(10005).sqrt()/f(n//14+1,k,m,l,x,1))[:n+1]]))or i<n and d.Decimal(((k**3-16*k)*m//i**3)*(l+545140134))/(x*-262537412640768000)+f(n,k+12,(k**3-16*k)*m//i**3,l+545140134,x*-262537412640768000,i+1)or 13591409

Thanks to O.O.Balance to push me to go deeper into this challenge: it was very fun and interesting.
Old answer, based on math.pi (74 bytes)
import math;f=lambda n:sum([ord(i)%2and int(i)for i in"%.*G"%(n,math.pi)])

Try it online!
N.B.: math.pi is "The mathematical constant π = 3.141592…, to available precision.", but the digits for the challenge are computed on the fly through the rounding and truncating operations. So, technically this is not a violation of the rules: "all the digits of pi must be calculated by your program at runtime."

The problem with your answer is that the maximum n it supports is way too small. https://repl.it/repls/WittyPoliteBug

@O.O.Balance I think I've fixed the problem. Thanks for the review.

Looks good now :-)

There's some great work here. Please vote for the question to be reopened.

@YiminRong This can be trivially ported to the other challenge.

Mathematica, 53 bytes
Total@DeleteCases[(RealDigits@N[Pi,#])[[1]],_?EvenQ]&

Mathematica noob, please excuse me.

I don‘t think that should count as "calculating all the digits of pi by your program".

It is frequently done in other pi-related challeanges, I don't see the problem.. Mathematica is generating pi to the number of digits requested, I believe the poster meant to not use any hardcoded constants.

That's what I meant, @sagiksp, updated the post to clarify that.

Ruby, 78 bytes
f=->n{"#{eval'x=2*10**n+p*x/=2+p+p-=1;'*x=p=4*n}"[0,n].bytes.sum{|c|c%2*c%16}}

Computes only to necessary precision.
Try it online!

Java 10, 275 bytes
import java.math.*;
n->{int j,t,i=0,s=3;for(;i<n-1;++i){var a=BigInteger.TEN.pow(10010).multiply(new BigInteger("2"));var p=a;for(j=1;a.signum()>0;p=p.add(a))a=a.multiply(new BigInteger(j+"")).divide(new BigInteger((2*j+++1)+""));s+=((t=(p+"").charAt(i+1)-48)%2)*t;}return s;}

Uses the approach from this answer by Kevin Cruijssen to calculate the digits. Try it online here.

SmallTalk – 299 bytes
Relies on the identity tan⁻¹(x) = x − x³/3 + x⁵/5 − x⁷/7 ..., and that π = 16⋅tan⁻¹(1/5) − 4⋅tan⁻¹(1/239), so no “cheats”.
|l a b c d e f g h p t s|l:=stdin nextLine asInteger. a:=1/5. b:=1/239. c:=a. d:=b. e:=a. f:=b. g:=3. h:=-1. s:=0. l timesRepeat:[c:=c*a*a. d:=d*b*b. e:=h*c/g+e. f:=h*d/g+f. g:=g+2. h:=0-h]. p:=4*e-f*4. l timesRepeat:[t:=p floor. p:=(p-t)*10. t odd ifTrue:[s:=s+t]]. Transcript show:s printString;cr

Save as pi.st. You provide the number of digits from the console when you run it, or through the <<< operator.
$ gst -q pi.st <<< 6
19

Python 3, 119 bytes
y=sum([int(i)for i in str(sum([4/j if j%4%3 else-4/j for j in range(10**8)if j%2]))[0:n+1].replace('.','')if int(i)%2])

My first code-golf submit, so please be gentle ._.
This computes pi on the fly using the Gregory-Leibniz series. X may prove insufficient for big n, but be aware of the RAMifications of choosing a large x.
Explanation:
Computation of Pi:
i.e.
sum( [4/i if i%4==1 else -4/i for i in range(x) if i%2==1] )
with x as large as sensible, also i%4==1 is one byte larger than i%4%3, so that's nice...
Casting a lot and eventual sum:
I'm sure there are better possibilities than all this, but I cast the float to string, take the first n*1 chars (including the dec point), replace the point, then cast each char in my string to int for the modulo check at the very end of the code, and take the sum of all i that are odd to assign them to y.
Room for improvement:
I'm sure there's lots, but I am the least happy with the series to compute pi, and also with the multiple int() casts ad the need to replace the dec point...
HELP

import math y=sum([int(i) for i in str(math.atan(1)*4)[0:n+1].replace('.','') if int(i)%2]) is 91 bytes and calculates pi using math.atan - probably well within the requirements of calculating pi on the fly, and the issue of lacking precision is gone i guess. if this is indeed legal calculation of pi in this task, i will replace it.

Any solution based on floating points will be wrong after about the 17th digit.

both math.pi and math.atan seem to manage 48 digits after the decimal point...same if it is casted to float. Instead of displaying the full decimal value, many languages (including older versions of Python), round the result to 17 significant digits - found that here,so maybe your reply is for python 2?

Sure, you can print out math.pi to an arbitrarily large amount of digits, but (after a point) those digits are incorrect! They are not the digits of pi.

print('{:10.100f}'.format(math.pi)) yields
3.1415926535897931159979634685441851615905761718750000000000000000000000000000000000000000000000000000, and for the non-zero digits, they are true to the value of pi if one looks them up. what other method would you recommend to use? if one computes them with a series (as my answer does) they will also be inaccurate after some number of digits, as is the nature of pi calculation as i understand it. if you suggest a more efficient series i am glad to consider it.

No they aren't. π ≃ 3.14159265358979323846.

Of course you can't compute it to infinite precision, but you must compute it to n digits of accuracy.

source: Stack Exchange

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

sort: votes


text:
Which Go rank is higher?
Players of the traditional board game Go measure their skill in a system of ranks:

Players new to the game are ranked 30th kyū (written 30k) and progress counting down to 1st kyū (written 1k). These are considered the student ranks.
A player may promote from 1st kyū to 1st dan rank (written 1d), and then progress counting up to 7th dan rank (written 7d). These are the master ranks.
Exceptionally skilled players past 7d may promote to the 1st professional dan rank 1p, and progress counting up to 9th professional dan rank (written 9p). This is the highest rank.

In short: ranks are ordered 30k < 29k < ··· < 1k < 1d < 2d < ··· < 7d < 1p < 2p < ··· < 9p.
Task
Given two strings among {30k, …, 1k, 1d, …, 7d, 1p, …, 9p} as input, output the higher rank of the two. (If they are equal, simply output either input.)
(As usual, I/O is flexible — your answer may be a function or a full program, reading input in any reasonable fashion and producing output in any reasonable fashion.)
This is code-golf: the objective is to minimize your code's byte count.
Test cases
(Format: input1 input2   output.)
29k 9k    9k
21k 27k   21k
6d 1p     1p
5d 17k    5d
1k 1d     1d
1d 1d     1d
1d 2d     2d
9p 1d     9p
2d 30k    2d
1p 1k     1p
1d 1p     1p
1p 2d     1p
7p 8p     8p
30k 30k   30k

Can the inputs require leading zeroes? I.e. 04k

No; while I'm flexible about I/O methods, I'm afraid I won't allow any variation in the input strings themselves — I think they are a perfect level of "interesting" as-is. (I won't allow 4 k or 4K or so, either.)

Are we allowed to take the inputs as (int, string) pairs?

No; again, the spirit of the challenge is to manipulate the exact text strings 30k, 29k, 1k, 1d et cetera, so I won't allow any variation there.

JavaScript (ES7), 58 bytes
Takes the two strings in currying syntax (a)(b).
a=>b=>(g=s=>parseInt(s)*'_dp'.search(s[1])**3)(a)>g(b)?a:b

Try it online!
How?
The helper function g() translate the input string s into a score.
1) We look for s[1] into the string "_dp". This gives:

1 for a dan rank "xd"
2 for a professional dan rank "xp"
-1 for a kyū rank "xk" or "xxk" because s[1] is either "k" or a digit

2) We cube this result, which leaves 1 and -1 unchanged but gives 8 for a professional dan rank.
3) We multiply by the decimal part of the rank.

Not as clever, but -3: a=>b=>(g=s=>parseInt(s)*~{d:~1,p:~8}[s[1]])(a)>g(b)?a:b

@FireFly Thanks. But a similar answer was already submitted and I'd better let this one unchanged.

Jelly,  11  10 bytes
Inspired to go back to the drawing board by Arnauld!
⁾kNyv9FµÞṪ

A monadic link accepting a list of strings* (as described) which yields the highest rank found.

Also works with numbers other than two.

Try it online! Or see a test-suite.
How?
Sorts by a key function and returns the rightmost (i.e. a maximum).
The key function first changes any k to become an N using the dyadic atom y, translate, with the two-character list ⁾kN (Jelly code for ['k','N']) and then evaluates the string as a monad with an argument of nine (by use of the code v9).
In Jelly:

N is a monadic atom which negates its input

so the code 9 30N does not actually use the nine and results in the integer -30

d is a dyadic atom which yields the result of a Python divmod of two values - the pair of the results of integer division and modulo

so the code 9 7d results in \$\lfloor \frac 7 9 \rfloor\$ paired with \$7 \pmod 9\$ which is [0,7]

p is a dyadic atom which performs a Cartesian product, which includes implicit 1-indexed range-ification of its inputs

so the code 9 p3 yields the Cartesian product of [1,2,3] and [1,2,3,4,5,6,7,8,9] which is [[1,1],[1,2],...,[1,9],[2,1],[2,2],...,[2,9],[3,1],[3,2],...,[3,9]]

Once such evaluations have been performed using the strings to be compared we need to be able to compare the results; since ints are not comparable with lists we need to wrap the negated values in a list, but since the ordering would still work after the p lists are flattened (e.g. [[1,1],[1,2],...] -> [1,1,1,2]) we can use the single-byte monadic atom F applied to all evaluations.
A table of the inputs to their key-value is as follows:
in    ⁾kNy    ⁾kNyv9F
30k   30N     [-30]
29k   29N     [-29]
...
 2k    2N     [-2]
 1k    1N     [-1]
 1d    1d     [0,1]
 2d    2d     [0,2]
 ...
 6d    6d     [0,6]
 7d    7d     [0,7]
 1p    1p     [1,1,1,2,...,1,9]
 2p    2p     [1,1,1,2,...,1,9,2,1,...,2,9]
 ...
 8p    8p     [1,1,1,2,...,1,9,2,1,...,7,9,8,1,...,8,9]
 9p    9p     [1,1,1,2,...,1,9,2,1,...,7,9,8,1,...,8,9,9,1,...,9,9]

Code commentary:
⁾kNyv9FµÞṪ - Link: list of lists of characters
       µÞ  - sort by (Þ) the monadic link to the left (µ):
⁾kN        -   two-char list = ['k', 'N']
   y       -   translate the current string (change 'k's to 'N's)
     9     -   literal nine
    v      -   evaluate (the left) as Jelly code with the input as given on the right (9)
      F    -   flatten the result
         Ṫ - tail (get the rightmost, and hence (a) maximum)

I knew it had to happen. :) But I'm satisfied anyway to have outgolfed you for about 90 minutes. :p

I still feel a little silly for not having explored an avenue which came to my mind right at the start!

This is a really neat answer!

Jelly, 14 bytes
OṪ|8-2*×ṖV$µÞṪ

A monadic link accepting a list of strings* (as described) which yields the highest rank found.
* Also works with numbers other than two.
Try it online! Or see a test-suite.
How?
Sorts by a key function and returns the rightmost (i.e. a maximum).
The letters \$k\$, \$d\$, and \$p\$ have ordinals \$107\$, \$100\$, and \$112\$ respectively. In binary \$107\$ has the eight bit set while the others do not, hence bitwise OR-ing with 8 we get 107, 108, and 120 respectively - these are now in the order required to sort the classes they identify.
The amateur ranks are in descending order so to complete our key function we could concatenate our class-identifier with the number given in the string, negating if the string ends in \$k\$ (e.g. '7k' -> [107,-7] while 7p -> [120,7]). In the code this requires storing the class-identifier and exponentiating minus one for the multiplication - OṪ|©8-*×ṖV$®,µÞṪ - which is \$16\$ bytes.
To improve upon this we can use minus two instead and multiply the elements together, which may be achieved with a single multiplication.
OṪ|8-2*×ṖV$µÞṪ - Link: list of lists of characters
           µÞ  - sort by (Þ) the monadic link to the left (µ):
O              -   ordinals
 Ṫ             -   tail
   8           -   literal eight
  |            -   bitwise OR
    -2         -   literal minus two
      *        -   exponentiate
          $    -   last two links as a monad (of the current list of characters):
        Ṗ      -     pop (get all but rightmost character)
         V     -     evaluate as Jelly code (gets the number)
       ×       -   muliply
             Ṫ - tail (get the rightmost)

Table of the key-function's action...
in    ṖV$  OṪ   OṪ|8       OṪ|8-2*×ṖV$
30k   30   107  107       -4867778304876400901747340308643840 = ((-2)^107)*30
29k   29   107  107       -4705519028047187538355762298355712 = ((-2)^107)*29
...
 2k    2   107  107        -324518553658426726783156020576256 = ((-2)^107)*2
 1k    1   107  107        -162259276829213363391578010288128 = ((-2)^107)*1
 1d    1   100  108         324518553658426726783156020576256 = ((-2)^108)*1
 2d    2   100  108         649037107316853453566312041152512 = ((-2)^108)*2
 ...
 8d    8   100  108        2596148429267413814265248164610048 = ((-2)^108)*8
 9d    9   100  108        2920666982925840541048404185186304 = ((-2)^108)*9
 1p    1   112  120     1329227995784915872903807060280344576 = ((-2)^120)*1
 2p    2   112  120     2658455991569831745807614120560689152 = ((-2)^120)*2
 ...
 8p    8   112  120    10633823966279326983230456482242756608 = ((-2)^120)*8
 9p    9   112  120    11963051962064242856134263542523101184 = ((-2)^120)*9

Very nice method!

I din't understand how 14 unicode chars translate to 14 bytes. 2^8=256, which is ASCII, afaik. Don't you need more than one byte to hold unicode chars, then?

@PostSelf the bytecode is 14 bytes, the unicode characters just represent single bytes - see the codepage in the link to "bytes" in the header.

@JonathanAllan Ah, I see, thank you!

MATL, 30 28 25 23 bytes
,0&)Uw'k'-tUw6*+qw*w]<G

Try it online!
(-2 bytes thanks to Luis Mendo)
(another -3 bytes replacing v&X> with >, this time based on Luis Mendo's answer)
(-2 bytes using &) syntax)
Explanation:

Subtract 'k' from the last character (gives n = -7, 0, 5 for 'd', 'k', 'p' respectively).
Compute v = n^2+6n-1 (gives 7, -1, 54 respectively).
Multiply that value v by the actual rank number (so k levels get negative products, d levels go from 7 to 49, p levels from 54 and above).
Do that for both input strings, compare the products
Get the input string corresponding to the greater product

Alternate, more straightforward method: :
23 bytes
,0&)Uwo'dpk'1L6^XE*w]<G

Try it online!
,              % Do this twice (once for each input):
0&)            % Split the input into number, last letter
U              % str2num i.e. Convert '21' to 21
wo             % Bring the letter out and change it to numeric (its ASCII code)
'dpk'          % Push the array 'dpk'
1L6^           % Push [1 2 1j] and raise to ^6, giving [1 64 -1]
XE             % Find the letter in 'dpk', replace it with its corresponding
               %  number from the second array (d=1, p=64, k=-1)
*              % Multiply the number part by this
w              % Switch to bring out the other input to top
]              % End loop
               % Stack has [second input's value, first input's value]
<              % Is second input < first input? 1 or 0
G              % Retrieve the corresponding input: 1 for 1st input,
               %  0 for last (2nd) input

So now we have Arnauld's algorithm and the sundar polynomial method. Lovely. +1

Haskell, 73 71 bytes
r(x,"k")=0-x
r(x,"d")=x
r(x,"p")=x+7
p=r.head.reads
a#b|p a<p b=b|0<1=a

Try it online!
As usual for me, just a straight forward implementation rather than anything particularly golf-y. The "(#)" function takes two ranks as strings and returns the larger one. Works only with exactly the format specified in the question.
(I've also tried a version using comparing and maximumBy but that ended up being 3 bytes longer - damn you base and your occasionally human readable function names!)
(Applied suggestions by Amphibological and Lynn)

You can save 1 byte by changing True to 1<2.

(-x) can be 0-x.

you could change the final pattern match to r(x,_)=x+7, too!

You forgot to change the final pattern match, it should be 69 (also 0-x can be  -x)

Python 2, 54 bytes
lambda s:max(s,key=lambda x:(int(x,27)%9-3)*int(x,26))

Try it online!
Using Arnauld's method. The mapping int(x,27)%9-3 only depends on
the last letter of x, since all digits but the last one contribute a multiple of 9. It takes:
'k' -> -1
'p' -> 1
'd' -> 4

This is barely enough margin for the multiplier for 1p to beat 7d, the highest dan rank, when interpreted in base 26.
I found this by brute-forcing expressions of this form and a few others.

Python 2, 64 bytes
lambda s:max(s,key=lambda x:(ord(x[-1])|8,int(x,36)^-('k'in x)))

Try it online!

R, 73 bytes
function(v)v[rank(as.double(chartr('dp','.0',sub('(.+)k','-\\1',v))))][2]

Try it online!

I had to use as.double instead of strtoi since the latter doesn't handle spaces/points and I couldn't think of another valid character to replace d
-1 byte using rank instead of order, since they are equal when there are only two elements

I tried using L in place of . but it's not working either... will let you know if I can find something else.

Congrats on 1K!

Jelly, 13 bytes
This is quite different from my other Jelly answer, so I'm posting this separately.
Takes input as a list of two (or more) strings.
“kNdHp0”yVµÞṪ

Try it online!
Commented
“kNdHp0”yVµÞṪ
“kNdHp0”       - literal string
        y      - translate each rank character into a Jelly instruction/symbol:
                   'k' -> 'N' = negate
                   'd' -> 'H' = halve
                   'p' -> '0' = a literal zero
         V     - evaluate as Jelly code
                 examples:
                   '21k' -> '21N' -> -21
                   '7d'  -> '7H'  -> 3.5  (*)
                   '3p'  -> '30'  -> 30
          µÞ   - sort the input using all of the above
            Ṫ  - return the second entry

(*) we don't really need to halve these values, but we do want to get rid of 'd'

Oh man, I thought about exactly this kind of method before going down the route I did... I really should have tried it out!

...now I have 11

Julia 0.7   100 93 bytes
It is not the most efficient way, (contrast @sundar's Julia 0.6 answer)
but it is nice because it is purely numeric.
Also uses dispatch (though sadly only once)
!z=(-z%2+.9)z*z
s(x,y,z)=(10x+y)*!z
s(x,z)=x*!z
~r=s(Int[r...]...)
a|b=[a,b][argmax(.~[a,b])]

Very similar code works in 0.6 Try it online
How:
The trick is all in the !(z) function.
It maps the UTF-8 value:

for k into a negative number, so it sorts backwards
for d into a positive number
for p into a larger positive number

As shown:
julia> !(Int('k'))
-1144.8999999999996

julia> !(Int('d'))
9000.0

julia> !(Int('p'))
11289.6

Test results
julia> @testset "Check it" begin
               @test "29k" | "9k" == "9k"
               @test "21k" | "27k" == "21k"
               @test "6d" | "1p" == "1p"
               @test "5d" | "17k" == "5d"
               @test "1k" | "1d" == "1d"
               @test "1d" | "1d" == "1d"
               @test "1d" | "2d" == "2d"
               @test "9p" | "1d" == "9p"
               @test "2d" | "30k" == "2d"
               @test "1p" | "1k" == "1p"
               @test "1d" | "1p" == "1p"
               @test "1p" | "2d" == "1p"
               @test "7p" | "8p" == "8p"
               @test "30k" | "30k" == "30k"
       end
Test Summary: | Pass  Total
Check it      |   14     14
Test.DefaultTestSet("Check it", Any[], 14, false)

This is pretty neat. And I didn't know .~[a,b] was possible! Btw, I think you can replace collect(r) with [r...] and save a few bytes.

Nice, and then i can ise Int[r...] rather than Int([r...]) to save a few more. Thanks

By the way, the code works pretty much as is (after replacing argmax with indmax) on Julia 0.6 as well. You can include a Try it online! link if you wish to.

Thanks, my other recent julia 0.7 answers suffered much more from avoiding depredations than this one did.

Yeah, things do seem to be moving towards a more verbose direction in Julia land with required keyword args, stdlib moves requiring imports, necessary whitespaces, etc. Btw, did you mean 0.7 answer attempts that you ended up posting as 0.6 answers? I took a glance around from your userpage, but didn't find any Julia 0.7 answers.

Hmmm, no seems not. Maybe I was thinking of an earlier version of this answer. Or possibly even a 0.5->0.6 deprecation

Haskell, 64 bytes
r[(n,[c])]=n*(gcd(fromEnum c)28-3)
g=r.reads
a%b|g a>g b=a|1>0=b

Try it online!
The expression gcd(fromEnum c)28-3 maps the character to a multiplier
k -> -2
d -> 1
p -> 25

The idea was to take their character values [107,100,112] and choose 28 to have increasingly large factors in common with them, made easy but the first one 107 being prime. This method saves 2 bytes over explicitly writing out a mapping.
The built-in reads is used to separate the number and rank.
Prelude> reads "25k" :: [(Int, String)]
[(25,"k")]

(Actually, the direct splitting from Sara J is one byte shorter, giving 63 bytes.)
The take-the-bigger-one step takes an annoying number of bytes in my solution. I tried other approaches like iterating over ranks in decreasing order and taking the first one that's an element of [a,b], but they turned out longer.

MATL, 28 27 bytes
,w'kdp'X{'*-1 . *8'YbYXU]>G

Try it online! Or verify all test cases.
Explanation
,                % Do twice
  w              %   Swap. Takes implicit input
  'kdp'          %   Push this string
  X{             %   Split chars: gives cell array {'k', 'd', 'p'}
  '*-1 . *8'     %   Push this string
  Yb             %   Split at whitespace: gives cell array {'*-1', '.', '*8'}
  YX             %   Regexprep: replaces 'k' by '*-1', 'd' by '.', 'p' by '*8'
  U              %   Convert to number: evaluates string
]                % End
>                % Greater than? Gives a result r which is 0 or 1
G                % Push r-th input (modularly: 0 is last, 1 is first)

Jelly, 16 bytes
Takes input as a list of two strings.
OṪ²²%90’’×ṖV$µÞṪ

Try it online! (all test cases)
How?
The ASCII code \$c\$ of the rank character is converted to a multiplier \$m\$ with the following formula:
$$m=(c^4 \bmod 90)-2$$
Which gives:
 char. | ASCII | **4       | mod 90 | -2
-------+-------+-----------+--------+----
  'k'  |   107 | 131079601 |      1 | -1
  'd'  |   100 | 100000000 |     10 |  8
  'p'  |   112 | 157351936 |     76 | 74

Commented
OṪ²²%90’’×ṖV$µÞṪ
OṪ²²%90’’        - process the rank character        takes a string, e.g. '28k'
O                - get ASCII codes                   --> [50, 56, 107]
 Ṫ               - pop                               --> 107
  ²²             - square twice                      --> 131079601
    %90          - modulo 90                         --> 1
       ’’        - decrement twice                   --> -1
          ṖV$    - process the decimal part
          Ṗ      - remove the last character         --> '28'
           V     - evaluate as Jelly code            --> 28 (integer)
         ×       - multiply                          --> -28
             µÞ  - sort input using all of the above
               Ṫ - return the second entry

Alternate formula
For 16 bytes as well, we can use:
$$m=((c-1)^9 \bmod 64)-1$$
OṪ’*9%64’×ṖV$µÞṪ

Try it online!

JavaScript (ES6), 55 54 bytes
-1 byte thanks to @Shaggy
a=>b=>(s=x=>parseInt(x)*~-{p:9,d:2}[x[1]])(a)>s(b)?a:b

Try it Online!
Explanation
a=>b=>
  (s=x=>                 // Function s(x), that converts x to a "score", where a higher
                         // rank gets a higher score by
    parseInt(x)*         //   taking the integer part of x and multiplying it by
    ~-{p:9,d:2}[x[1]])  //   ~-9 (8) if it is a professional rank, ~-2 (1) if it is a dan
                         //   rank and ~-undefined (-1) if it is a kyū rank by looking up
                         //   the second character of the string
  (a)>s(b)               // Compare s(a) and s(b)
    ?a:b                 //   and return the one with the biggest score

This seems to work for 54.

@Shaggy your solution fails for f("2d")("1d"), but replacing d:1 with d:2 fixed it.

Try to golf this, but still 54 bytes... so sad

53

but seems quite different, join or sep?

@l4m2 It looks very different to me

Perl, 46 38 bytes
s/p/0/g;s/\w*k/-$&/g;/ /;$_=@F[$`<=$']

Run as perl -pae '...'. Takes input from stdin, space-separated on one line.
Performs a couple of substitutions to make comparison "easier". The regexp substitutions essentially amount to replacing p with a trailing zero and k with a leading negative sign. So 10k becomes -10 (so the ordering is reversed) and 3p becomes 30 (so that p is above everything else). Then it's just a simple numerical comparison.
Thanks to DomHastings for the $` / $' suggestion, which shaved off 8 bytes.

Really nice solution, I didn't come up with anything quite so elegant! You can save 8 bytes, by matching on / / and using $`  and $': Try it online!

Ruby, 52 46 bytes
->s{s.max_by{|x|(5-x[-1].ord*5%8)*x.to_i(26)}}

Straightforward port of xnor's answer.
Try it online!
EDIT: -6 bytes by realizing I could have used a Proc

was just about to comment the change you just made :) you don't have to duplicate the code here, just use the header

C# (Visual C# Compiler), 136 135 bytes
a=>b=>{string c=a.PadLeft(3,'0'),d=b.PadLeft(3,'0');int x=c[2]-d[2];return(x==0?c.CompareTo(d)*(c[2]=='k'?-1:0)>0:x==5|x>9|x==-7)?a:b;}

Try it online!
-1 Byte thanks to TheLethalCoder
Expanation:
static void Main()
{
    System.Func<string, System.Func<string, string>> f =
        a => b =>
        {
            string c = a.PadLeft(3, '0'),
                d = b.PadLeft(3, '0');      //Pad the input with leading '0' to be 3 characters long
            int x = c[2] - d[2];            //Calculate the difference of the letter characer (the character at index 2) as integer
            return                          //return ...
                (x == 0 ?                   //if the letter of the inputs is the same...
                c.CompareTo(d)              //  compare the padded strings resulting in a positive number if the first input is greater or a negative number if the first input is lower
                    * (                     //  multiply the result by...
                    c[2] == 'k' ? -1 : 0    //  if the letter is 'k' then -1 else 0
                    ) > 0                   //  check if the result is greater than 0
                :                           //else (the letters are not the same)
                x == 5 | x > 9 | x == -7    //  check if the letter difference was 5 (input p and k) or 12 (> 9, input p and d) or -7 (input d and k)
                ) ? a : b;                  //  then return the first input else return the second input.
        }
    ;

    System.Console.WriteLine(f("29k")("9k"));
    System.Console.WriteLine(f("21k")("27k"));
    System.Console.WriteLine(f("6d")("1p"));
    System.Console.WriteLine(f("5d")("7k"));
    System.Console.WriteLine(f("1k")("1d"));
    System.Console.WriteLine(f("1d")("1d"));
    System.Console.WriteLine(f("1d")("2d"));
    System.Console.WriteLine(f("9p")("1d"));
    System.Console.WriteLine(f("2d")("30k"));
    System.Console.WriteLine(f("1p")("1k"));
    System.Console.WriteLine(f("1d")("1p"));
    System.Console.WriteLine(f("1p")("2d"));
    System.Console.WriteLine(f("7p")("8p"));
    System.Console.WriteLine(f("30k")("30k"));
}

You can save a byte with currying i.e. a=>b=>.

Oh also you should be able to use ints instead of the literal chars. Been a while since I've golfed so these are gonna keep coming to me...

@TheLethalCoder: PadLeft requires char and using 107 instead of 'k' makes no difference.

Oh I thought the conversion was implicit... worth a try though

Java 8, 128 122 121 bytes
a->b->g(a)<g(b)?b:a;float g(String s){return("kdp".indexOf(s.charAt(s.length()-1))-.9f)*new Byte(s.replaceAll(".$",""));}

-6 bytes thanks to @SaraJ.
Try it online. (See the bottom of the output in the TIO-links to see the mapped values.)
Explanation:
a->b->                       // Method with two String parameters and String return-type
  g(a)<g(b)?                 //  If the mapped value of `a` is smaller than `b :
   b                         //   Return input `b` as result
  :                          //  Else:
   a;                        //   Return input `a` as result

float g(String s){           // Separated method with String parameter and float return-type
                             // (This method maps all possible Strings to a value)
 return("kdp".indexOf(       //   Take the (0-based) index in the String "kdp"
    s.charAt(s.length()-1))  //   of the last character
    -.9f)                    //   After we've subtracted 0.9
  *(                         //  And multiply it with:
    new Byte(                //   Convert the String to an integer
     s.replaceAll(".$",""));}//   After we've removed the trailing character

122 bytes

@SaraJ Thanks! :) I had trouble finding a shorter alternative, but I was sure there should be some. Was pretty funny I found three alternative 128-byters, but none shorter..

R, 65 62 bytes
`+`=paste0;max(ordered(scan(,""),c(30:1+"k",1:7+"d",1:9+"p")))

Try it online!
This comes in a bit shorter than the previous R answers, and makes good use of R's statistics functionality :-)
-3 bytes thanks to Robin Ryder's suggestion to use ordered instead of factor
Takes input from stdin (although the TIO link restructures slightly for ease of testing). Casts the input ranks into an ordered factor of all ranks, then takes the max.
Output looks like:
[1] MAX_RANK
46 Levels: 30k < 29k < 28k < 27k < 26k < 25k < 24k < 23k < 22k < ... < 9p

62 bytes with ordered(...) instead of factor(...,o=T)

@RobinRyder thanks! I read through the docs page for factor and completely missed out on ordered.

Haskell, 75 70 68 bytes
g k=read(init k)*(abs(fromEnum(last k)*2-209)-8)
a?b|g a>g b=a|1>0=b

Try it online!
-5 bytes thanks to @Laikoni
-2 bytes thanks to @ASCII-only

(read$init k) can be read(init k). An auxiliary function is shorter than using case of: Try it online!

68?

@SaraJ you haven't updated the link :P

sadly returning a tuple doesn't work because you need the * to negate for k

and something like fromMaybe -1$elemIndex" d       p" is too long :|

My solution is 66 bytes and very similar :) keep golfing down that expression!

Python 2, 61 bytes
Uses Arnauld's algorithm.
lambda c:max(c,key=lambda i:'_dp'.find(i[-1])**3*int(i[:-1]))

Try it online!

Previous answer, 73 bytes
lambda c:max(c,key=lambda i:('dp'.find(i[-1]),int('-'*('k'in i)+i[:-1])))

Try it online!

Julia 0.6, 75 71 65 bytes
S->S[indmax((s->parse(s[1:end-1])*(search("_dp",s[2])-1)^3).(S))]

Try it online!
(-4 bytes, parse in 0.6 auto detects it as Int)
(-6 bytes, use (search("_dp",s[2])-1)^3) based on Arnauld's JS answer, instead of ((c=cmp(s[end],'k'))^2*6+4c-1))

Retina 0.8.2, 29 bytes
O$^`.+((k)|(.))
$3$&$*1$2
1G`

Try it online! Accepts any number of ranks and outputs the highest. Explanation:
O`

Sort the lines...
$

... using the specified key...
^

... in reverse order.
.+((k)|(.))
$3$&$*1$2

The key is created from the input line as follows: a) the (professional) dan rank letter b) the rank in unary c) the kyū letter (where applicable). Since this is a reverse sort, the professional dan rank p sorts before the dan rank d and the kyū rank which starts with 1 because the kyū letter is left at the end. Within the ranks the (professional) dan ranks sort in descending order due to the length of the unary string, however the trailing k for the kyū ranks causes them to sort in ascending order.
1G`

Select the first line, which is now the greatest rank.

J, 39 bytes
[`]@.(<&(".@}:((*_1&^)+]*0=2&|)a.i.{:))

Try it online!
explanation
[`]@.(<&(".@}: ((* _1&^) + ] * 0 = 2&|) a. i. {:))    entire phrase
[`]@.(<&(       assign a rank number            ))
      <&                                              "less than" of the ranks of the left and right args
                                                      ie, return 1 if right arg rank is bigger
[`]@.                                                 if so, return right arg.  otherwise left
                                                      breaking down the rank number part now...
        (       assign a rank number            )
        (".@}: ((* _1&^) + ] * 0 = 2&|) a. i. {:)
        (".@}:                                  )     everything but last char, convert to number
        (                               a. i. {:)     index within ascii alphabet of the last char
                                                      these become the left and right args to what follows...
        (      ((* _1&^) + ] * 0 = 2&|)         )
        (      (           ] * 0 = 2&|)         )     the right arg * "is the right arg even?"
                                                      because only 'k' is odd (107), this will be 0 for 'k'
                                                      and will be 100 for 'd' and 112 for 'p'
        (      ((* _1&^)              )         )     left arg (number of go rank) times _1 raised
                                                      to the ascii index.  this will swap the sign
                                                      for k only, hence producing a valid rank function

Python, 59 bytes
lambda s:max(s,key=lambda x:(-2)**(ord(x[-1])|8)*int(x,26))

An unnamed function accepting an iterable of strings which returns a maximal one by Go rank. Works much like my Jelly answer (just uses the whole string evaluated as an integer in base 26 to save bytes).
Try it online!

C (gcc), 95 91 bytes
Thanks to ceilingcat for the suggestion.
#define _(s)(strtol(s,&u,0)+(*u^15)*8)*~-(*u&2)
f(s,t,u)char*s,*t,*u;{puts(_(s)<_(t)?s:t);}

Try it online!

Good use of old-style function declarations.

PHP, 100 98 bytes
(-2 bytes by changing function declaration)
<?function f($v){return(strpos('!!dp',$v[1])-1)**3*$v;};list(,$a,$b)=$argv;echo f($a)>f($b)?$a:$b;

To run it:
php -n <filename> <rank1> <rank2>

Example:
php -n go_rank.php 1p 7d

Or Try it online!

PHP (7.4), 74 bytes
$g=fn($v)=>(strpos(__dp,$v[1])-1)**3*$v;$f=fn($a,$b)=>$g($a)>$g($b)?$a:$b;

Try it online!

How?
Similar to Arnauld's approach, but in PHP. I'm using a function to convert each rank to a numeric value and compare and output the one with higher value.
The rank value comes from input string's second character position in !!dp, reduced by one and then powered to 3 and multiplied by the integer part of input string.
So for example, position of second character of 1p which is p in !!dp is 3, reduced by one and powered to 3 it will be 8. So integer part of all *p ranks will be multiplied by 8. This means 1p = 8, 2p = 16, ..., 9p = 72.
For all *d ranks, the integer part will be multiplied by 1 (or simply no multiplication). This means 1d = 1, ..., 7d = 7.
And for all *k and **k ranks, position of second character in !!dp will be equal to false which is equal to 0 (no explicit), reduced by one and powered by 3, means that the integer part will be multiplied by -1. This means 30k = -30, ..., 1k = -1.
This is my first ever golf attempt, not sure how bad it is!

Perl 6, 35 bytes
*.max: {{+TR/pd/0 /}(S/(.+)k/-$0/)}

Try it online!
A rather different approach to all this search-and-cube stuff. Essentially string replacement: ...k -> -..., p -> 0, d deleted. Thus newbies get a minus score, dans get their rank and pros get rank*10. Using the WhateverStar gets us a closure, and max takes a comparator function.

05AB1E, 12 bytes
Σ'pK°.Vyþ*}θ

.V (evaluate as 05AB1E code) is inspired by @Arnauld's similar approach in his Jelly answer.
Input as a list of two (or more) of the valid strings.
Try it online or verify all test cases.
Explanation:
Σ           # Sort the (implicit) input-list by:
 'pK       '#  Remove "p"
    °       #  Take 10 to the power this string
            #  (if the string is not a valid number, the string remains unchanged)
     .V     #  Evaluate the string as 05AB1E code
       yþ*  #  Multiply it with just the digits of the string
}θ          # After the sort: pop and push the last element
            # (which is output implicitly as result)

k pops a list and an item, and pushes the index of the item in the list. Since the stack doesn't contain a list, "30k" through "1k" will always result in -1. Multiplied by their number, it will be the range \$[-30,-1]\$.
d checks if a number is non-negative (>= 0). Since this is truthy for all, "1d" through "7d" will always result in 1. Multiplied by their number, it will be the range \$[1,7]\$.
Removing p and using ° will use 10 to the power \$a\$ if \$a\$ is a valid number, otherwise \$a\$ remains the same. So if \$a\$ is "2k" or "2d", it will remain "2k" or "2d" after using °, but "2" (since we've removed the p) with ° will become 100. Multiplied by their number, "1p" through "9p" will be the list \$[10, 200, 3000, 40000, 500000, 6000000, 70000000, 800000000, 9000000000]\$.

See all mapped values here.

Scala, 307 61 54 bytes
Great thanks to Kevin Crujissen and ASCII-only for their work on this answer, that got it down ~250b.
Some algorithm optimization for ordering function of 61b answer.
l=>l.maxBy(s=>s.init.toInt*(math.abs(s.last*2-209)-8))

Test cases here: Try it online!
Old 61 bytes answer
l=>l.maxBy(s=>(s.init.toInt+s.last)*(math.abs(s.last-105)-3))

The trick is to compute distance between rank letter and i (char code 105). Then we use this distance as a score (dan = 5, kyu = 2, pro = 7) that we multiply by the rank number. Then we take the maximum on input array using the scoring function.
Try it online too!

I don't know Scala too well, but with some basic golfs I got it down to 266 bytes.

Also, doesn't Scala have lambdas as well to golf def f(a:String,b:String)= to a=>b=>?

@KevinCruijssen thanks for your help
as for your second comment, well, I tried that in tio but it didn't compile, telling me to add typing :(

@V.Courtois you can add typing in the header

As @ASCII-only mentioned, you can create it in the header with a var f: Function1[String, Function1[String, String]]=. 247 bytes.

203

188 bytes by returning a variable.

86

@ASCII-only Yeah, that might be a better approach. :D Whopping -102 bytes right there.

64

62

61

56

Sadly that 56 doesn't work on test case 3 (6d,1p should return 1p).

54?

how :o nice one

[R], 87 [bytes]
update:
using JayCe's golfing:
f=function(v,k=-1,d=1,p=10)v[eval(parse(t=paste(gsub('(\\D)','*\\1',v),collapse='<')))+1]
[R], 150 [bytes]
a long answer I find interesting could save a lot of bytes if not sticking to the eval method.
inputs are s1 and s2:
f=function(s)eval(parse(t=sub("\\D+",paste0("*",c(-1,1,8)[match(tail(el(strsplit(s,"")),1), c("k","d","p"))]),s)));c(s1,s2)[as.numeric(f(s1)<f(s2))+1]

The eval approach can be improved like this ->
Try it online!

nice! evaluating the inequality..... I did not think of that :)

You can update your answer based on @digEmAll's comment - nice approach!

And it's golfable to 87 chars due to R's lexical scoping rules.

Excel VBA, 129 bytes
An anonymous VBE Immediate window function that takes input in range A1:A2 and outputs to the console.
[B:B]="=SUBSTITUTE(A1,""p"",10)":[C1:C2]="=LEFT(B1,LEN(B1)-1)*-1^IFERROR(FIND(""k"",B1)>0,0)":?[INDEX(A:A,MATCH(MAX(C:C),C:C,0))]

Explaination
[B:B]="=SUBSTITUTE(A1,""p"",10)"                            ''  Convert inputs to numerics
                                                            ''  by appending 10 to rank if
                                                            ''  is a professional dan rank
                                                            ''
[C1:C2]="=LEFT(B1,LEN(B1)-1)*-1^IFERROR(FIND(""k"",B1)>0,0)"''  Remove rightmost char; If
                                                            ''  the rank is kyū, then mult
                                                            ''  by -1
                                                            ''
?[INDEX(A:A,MATCH(MAX(C:C),C:C,0))]                         ''  Find the max, return the
                                                            ''  corresponding input

R, 83 bytes
function(v,k=-1,d=1,p=10)v[1+do.call("<",as.list(parse(t=gsub('(\\D)','*\\1',v))))]

Try it online!
Inspired by this answer by Zahiro Mor and digEmAll's comments on the answer. OP has not replied to comments so I am posting it separately, adding my own do.call twist to it to save additional bytes.
do.call takes arguments to the function as a list, which we do not need to eval before passing - a parse is sufficient. Still, as.list costs a lot of bytes...
Still not as golfy as digEmAll's chartr answer!

JavaScript (Node.js), 53 bytes
a=>b=>(s=([d,q])=>-d+q|{p:8*d,d,k:-d}[q])(a)>s(b)?a:b

Try it online!
Optimized from tsh's comment in https://codegolf.stackexchange.com/a/168183/

Kotlin, 256 bytes
{f:String,s:String->{val e={s:String->if(s.length==2)"0$s"
else s}
val d=e(f)
val n=e(s)
val r=when(d[2]){'k'->if(n[2]=='k')if(d<n)f
else s
else s
'd'->if(n[2]=='d')if(d<n)s
else f
else if(n[2]=='k')f
else s
else->if(n[2]=='p')if(d<n)s
else f
else f}
r}()}

Try it online!

46

Jelly, 20 bytes
6ị⁾kpiị“¡µ£‘’×ṖV$µÞṪ

Try it online!

Pyth, 15 bytes
Also uses Arnauld's method.
eo*^x"_dp"eN3.v

Try it here!

Go, 168 bytes
import(."strings"
."strconv")
func g(s string)int{i:=IndexByte("_dp", s[1])
j,_:=Atoi(s[:len(s)-1])
return i*i*i*j}
func f(a,b string)string{if g(a)>g(b){b=a}
return b}

Translation of Arnaud's idea. Had to be done for the sake of the language name.

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


text:
Double a number's continued fraction
Your task is, given x, output 2*x. Easy right!? But there's a catch: x will be given as a (possibly infinite) continued fraction, and the output must be a continued fraction. The input is guaranteed to be a real algebraic number whose degree is at most 2.
Input: The continued fraction of x. This is split into 3 parts: the integer part, the prefix, and the repeating part. The integer part consists of a single integer. The prefix and repeating part are (possibly empty) arrays of positive integers which describe the prefix and repeating part of the continued fraction. For example, the input (3, [1], [2, 4]) represents the continued fraction [3; 1, 2, 4, 2, 4, ...].
If the repeating part is empty, that indicates a rational number. For example, (3, [1, 2], []) represents [3; 1, 2] = 11/3. You must accept both forms of a rational number (i.e. (3, [1, 1, 1], []), which is [3; 1, 1, 1] = 11/3 should also be valid input).
Output: Output the continued fraction of twice the input, in the same format as the input. If the output is rational, you may output either form of the continued fraction. As long as the answer is equivalent to the correct answer, it is fine; no "compression" is necessary, so the infinite part might be "unrolled" a little (e.g. [1; 4, 2, 3, 2, 3...] may be written (1, [4], [2, 3]) or (1, [4, 2, 3], [2, 3])). All answers must be exact.
Test cases: The exact form column is given for convenience.
Input               Exact Form       Output
(0, [] [])          0                (0, [] []) or (-1, [1], [])
(-5, [1, 1], [])    -4.5             (-9, [], []) or (-10, [1], [])
(3, [1, 2], [])     11/3             (7, [3], []) or (7, [2, 1], [])
(1, [], [2])        sqrt(2)          (2, [], [1, 4])
(-1, [2], [2, 1])   -1/sqrt(3)       (-2, [1, 5], [2, 6])

And finally a slightly larger test case to ensure precision: (0, [1], [6, 1, 3, 1, 42, 1, 3, 1, 6, 2]) --> (1, [], [1, 2, 1, 8, 1, 20, 1, 8, 1, 2, 1, 2]).
Shortest code wins!
Hint: You can perform arithmetic in a rather straightforward manner on continued fractions as described here. Doubling a continued fraction is just a special case of this algorithm (although the tricky part may be to find when the continued fraction repeats).

Would this work? https://tio.run/##y00syUjNTSzJTE78n2b73zk/ryQzrzQ1xa0oMbkkMz8v2kjLrSg/F0PcQTlW7X@aA6b64MKiEgej2P//dQuKMvNKAA

@Pavel No, you must be able to specify the input solely based on the integer, prefix, and repeating parts rather than as Sqrt[2].

Sorry, that was a mistake on my part. Here's the link with the actual continued fraction as input: https://tio.run/##y00syUjNTSzJTE78n2b73zk/ryQzrzQ1xa0oMbkkMz8v2kjLrSg/F0PcQTlW7X@aQ7WhTrVRba2ChlYClJmgkFmskFpYmlmWmJOaV6JQkq@QgKk7uLCoxMEoQUvz/3/dgqLMvBIA

You can read about the way Mathematica formats continued fractions here and here, it's {integer, ...prefix, {...repeating}}

@Pavel Yes then it should be fine

[3; 1, 1, 1] would be (3, [1, 1, 1], []) in the input format we're using - so the question should probably mention it in that format (in the third paragraph), just to ensure clarity.

What constraints are there on how minimised the output must be? E.g. would (-2, [1, 5, 2], [6, 2]) be acceptable output for input (-1, [2], [2, 1])? How about (-2, [1, 5, 2, 6, 2, 6], [2, 6])?

@PeterTaylor Yes that would be acceptable output as long as it is still equivalent to the correct answer.

Wolfram Language (Mathematica), 44 bytes
ContinuedFraction[2FromContinuedFraction@#]&

Try it online!
Mathematica has a builtin! Yay! Mathematica's builtin is super long. Aww.
Mathematica's continued fractions look like {integer, ...prefix, {...repeating}}
-1 thanks to JungHwan Min

You could omit the * because Mathematica's default delimiter, if there isn't one, is Times.

When your language has builtins for everything from Scrabble scoring to Goat recognition, some of their names will have to be "super long" by necessity. :)

@sundar No, Mathematica only has ~5000 builtins. It's possible to make each builtin 2 bytes at most (see Mthmtca)

@user202729 But Mathematica wouldn't have been so popular if it did that :P

JavaScript (ES6), 267 bytes
(n,f,r)=>eval(`f=[0,...f];e=i=[0,2,1,0];o=j=[];k=[];m={};while([a,b,c,d]=e,c|d&&o)i*m[s=i+m+(e=c*d&&(q=a/c|0)==(b/d|0)?(o.push(q),[c,d,a-c*q,b-d*q]):1/(++i,[p,...f]=f+f?f:(i=r[0],r),p)?[b,a+b*p,d,c+d*p]:[b,b,d,d])]?k+(o=k)?o=0:(m={})[s]=1:m[s]=1;[2*n+j.shift(),j,k]`)

Accepts 3 arguments (n = integer part, f = prefix, r = repeating part). Outputs
the three parts in an array. Try it online!
Explanation
It's a fairly direct implementation of the algorithm for computing
continued fraction arithmetic linked in the challenge here. Repeating terms are handled by storing intermediate matrices in a lookup table, waiting for a duplicate, and outputting the terms until the next appearance of that duplicate. It's inelegant and nearly doubles the bytes needed to handle continued fractions, but I could not think of a better alternative.
The leading term is computed separately to ensure that negative continued
fractions retain positive values for all terms barring the first.
To prevent false positives when awaiting a repeated cycle, the lookup table
stores the data as follows: <index of input repeating part><delimiter><matrix values>.
Note that the golfed version uses eval to save 1 byte.
(n, f, r) => {
    f = [0, ...f];                       // use a 0 to chop off the integer part
    e = i = [0,2,1,0];                   // e: matrix with starting values to handle doubling
                                         // 0 + 2x
                                         // ------
                                         // 1 + 0x
                                         // i: tracks index of repeating part; until the function loops through the
                                         // repeating part, i is equivalent to NaN
    o = j = [];                          // o: alias for group of terms currently being computed
                                         // j: output array of non-repeating terms
    k = [];                              // k: output array of repeating terms
    m = {};                              // lookup table
    while ([a,b,c,d] = e, c | d && o)    // destructure matrix
                                         // c | d stops loop if both a/c and b/d equal infinity
        i * m[s = i + m + (              // m also serves as the delimiter; JS will stringify it as "[object Object]"
                                         // if i equals a value that is coerced to NaN, this multiplication
                                         // will be falsy
            e = c * d && (q=a/c|0) == (b/d|0) // checks if either c or d is 0 to avoid converting an Infinity value to 0 using the bitwise or
                ? (o.push(q), [c, d, a - c*q, b - d*q]) // append the output term and compute the new matrix
                : 1/(++i, [p, ...f] = f+f ? f : (i=r[0], r), p) // 1/(... p) checks if p is a valid number
                                         // f+f is a short way to check length of f; if f is an empty
                                         // array, f+f = '' (which is falsy)
                                         // if f is empty, try to replace with r
                                         // if r is empty, the value of i will be set to undefined (e.g. NaN)
                    ? [b, a + b*p, d, c + d*p]
                    : [b,b,d,d]
            )
        ]                                // checks if matrix has been cached in lookup table
            ? k+(o=k)                    // if the repeating part of the output has a value...
                ? o=0                    // o serves as a flag to halt the loop
                : (m={})[s] = 1          // reset lookup table to only hold the first duplicate matrix
            : m[s] = 1;                  // otherwise flag a matrix as seen
    return [2*n + j.shift(), j, k]       // add the doubled integer part to the first term
}


text:
Pocket Pokemon Type Evaluator
Yes, I know there are already three pokemon type questions, but almost all submissions hardcode the whole chart in one way or other. I think it'd be nice to change the requirement a bit to encourage more shortcut-taking typically seen in decision problems.
Objective
Write a program/function/whatever that accepts the attacking type and the defending type(s), and output something that can be identified as the final multiplier.
As usual, you can see the pokemon type chart here. Also available as text form (copied from another question linked above and modified a bit):
                                            Attacking Type
                No  Fi  Fl  Po  Gr  Ro  Bu  Gh  St  Fr  Wa  Gr  El  Ps  Ic  Dr  Da  Fa
    Normal        1   2   1   1   1   1   1   0   1   1   1   1   1   1   1   1   1   1
    Fighting      1   1   2   1   1 0.5 0.5   1   1   1   1   1   1   2   1   1 0.5   2
D   Flying        1 0.5   1   1   0   2 0.5   1   1   1   1 0.5   2   1   2   1   1   1
e   Poison        1 0.5   1 0.5   2   1 0.5   1   1   1   1 0.5   1   2   1   1   1 0.5
f   Ground        1   1   1 0.5   1 0.5   1   1   1   1   2   2   0   1   2   1   1   1
e   Rock        0.5   2 0.5 0.5   2   1   1   1   2 0.5   2   2   1   1   1   1   1   1
n   Bug           1 0.5   2   1 0.5   2   1   1   1   2   1 0.5   1   1   1   1   1   1
d   Ghost         0   0   1 0.5   1   1 0.5   2   1   1   1   1   1   1   1   1   2   1
i   Steel       0.5   2 0.5   0   2 0.5 0.5   1 0.5   2   1 0.5   1 0.5 0.5 0.5   1 0.5
n   Fire          1   1   1   1   2   2 0.5   1 0.5 0.5   2 0.5   1   1 0.5   1   1 0.5
g   Water         1   1   1   1   1   1   1   1 0.5 0.5 0.5   2   2   1 0.5   1   1   1
    Grass         1   1   2   2 0.5   1   2   1   1   2 0.5 0.5 0.5   1   2   1   1   1
T   Electric      1   1 0.5   1   2   1   1   1 0.5   1   1   1 0.5   1   1   1   1   1
y   Psychic       1 0.5   1   1   1   1   2   2   1   1   1   1   1 0.5   1   1   2   1
p   Ice           1   2   1   1   1   2   1   1   2   2   1   1   1   1 0.5   1   1   1
e   Dragon        1   1   1   1   1   1   1   1   1 0.5 0.5 0.5 0.5   1   2   2   1   2
    Dark          1   2   1   1   1   1   2 0.5   1   1   1   1   1   0   1   1 0.5   2
    Fairy         1 0.5   1   2   1   1 0.5   1   2   1   1   1   1   1   1   0 0.5   1

You can choose to either accept one or two defending types (the final multiplier for two defending types is just the product of all individual defending types). Both types of submissions are allowed, as they will be in separate categories.
Rules
You can choose any methods of input in any form, structure or order (e.g ['fire',['water','rock']]/['fire',['rock']]/['fire','rock'], 'water,ice,ghost'/'water,ghost',dragondragonsteel/dragondragon).
The only requirement is that the types being inputted must be exactly their names and nothing else. Moreover, the types are required to be in one case only: so if you choose "psychic/Psychic/PSYCHIC", you must also use the same case for every other types (e.g "poison/Poison/POISON"). The available casing are: all lowercase, capital case and all uppercase.
You can output anything as long as different multipliers corresponds to one and only one output value.
There will not be two defensive types of the same type.
Standard loopholes are disallowed.
Shortest byte wins.

Can we use the two letter identifiers from the chart?

I'm not sure if other aliases to the types should be allowed, because while I want the challenge to be liberal to allow creative solutions, allowing different identifiers seems to open too many possibilities (and potential loopholes too).

So, this is It's super effective! with more relaxed I/O formats. Is that correct? Or am I missing another difference? (I don't think this qualifies as kolmogorov-complexity, BTW.)

@Arnauld Yes. People can still try hardcoding the whole matrix (even if it might not be the best approach anymore), so I decided to leave kolmogorov-complexity in ;-)

(and since there are more than 2 possible outputs, this is not a decision-problem either...)

I really can't see any advantage to taking two defenders; we'd just have an 18 by 306 matrix with a domain of {0, 0.25, 0.5, 1, 2, 4} instead.

@Arnauld decision problems aren't limited to binary decision. That's like saying classifications are binary only.

Our tag is about decision problems in computability theory: a problem where a yes-or-no question is posed.

If anyone is interested: here is a minimal perfect hash function to convert the types in uppercase to [0..17] in Jelly.

@Arnauld okay. Removed decision-problem and kolmogorov-complexity, added classification.

JavaScript (ES6), 270 246 221 bytes
Takes input in currying syntax (a)(d) where a is the attacking type as a string and d are the defending type(s) as an array of strings. The case doesn't matter.
Returns a standard multiplier as a float.
a=>d=>d.map(d=>r*='0124'["ijfkj{jjnjjJ[kefjiZj[jxnjni^YnEW]vfeziiz]okjZmniV{oZvjhjnmj[zrfijij[yVzej[jjejjhujkmjZkZ[fjiuznjjz~zjfRjbkjy".charCodeAt((i=g(d)*18+g(a))/3)>>i%3*2&3]/2,g=s=>parseInt(s,36)*64%97%54%21%18,r=1)&&r

Try it online! or check the whole chart
How?
The base-36 interpretation \$N\$ of a given Pokemon type string is converted to a coordinate \$C\$ in \$[0..17]\$ using the following minimal perfect hash function:
$$C=((((N\times64)\bmod97)\bmod54)\bmod21)\bmod18$$
The chart is rearranged using the new coordinates and encoded as a single string with 6 bits of payload data per character, using ASCII codes in the range \$[64..127]\$.
There are 2 bits per cell, so the length of the string is \$(18\times18\times2)/6=108\$ characters.
For the curious, here is the code that encodes the string.

ABAP, 691 bytes
Form-Subroutine taking two or three types in capital case
DATA:x(180) TYPE x,v type x,b,c.DEFINE g.GET BIT:&1 + 1 OF &2 INTO b,&1 + 2 OF &2 INTO c.SET BIT:7 of v TO b,8 of v TO c.IF v = 3.o = 2.ELSE.o = v / 2.ENDIF. END-OF-DEFINITION.
DEFINE f.FIND &1(3) IN'NorFigFlyPoiGroRocBugGhoSteFrrWatGraElePsyIceDraDarFai'RESULTS data(&2). END-OF-DEFINITION.
FORM f USING a d e.DATA(o) ='1.00'.x ='BAA8AAAAA0AE96AABA709A36A9EEA099E6A9BA90A99AAF2EA075EADFAAA09E7AB9AAA009A7AAAAE074D6799590AAF65DA690AAAA57E6A0AF6EB56EA0A6EA6A6AA09AAFAA9AE0BABAFAA6A0AAAA956FB0BAADAA8A709BA6EAA860'.f:a
r,d s.DATA(l) = r-offset / 3 * 2.DATA(n) = o.IF strlen( e ) > 2.f e t.n = ( t-offset / 3 ) * 5.g l x+n.n = o.ENDIF.o = ( s-offset / 3 ) * 5.g l x+o.o = o * n.WRITE o.ENDFORM.

Newlines in the code replace required spaces around the END-OF-DEFINITION keywords. Might as well keep it as "readable" as possible.
Can be called by another program like so:
REPORT z.
 PERFORM f USING 'Fighting' 'Normal' 'Rock'.

Result: 4
REPORT z.
 PERFORM f USING 'Ghost' 'Normal' ''.

Result: 0
REPORT z.
 PERFORM f USING 'Fairy' 'Poison' 'Normal'.

Result: 0.5
Explanation
Geez, what a monster. Far from competitive as per usual, but I learned a lot about handling bits and bytes in ABAP - or rather that I don't want to do it ever again.
The type weakness table was converted to bit pairs:
00 = 0x, 01 = 0.5x, 10 = 1x, 11 = 2x
Each row of the table was then converted to 5 bytes each and all concatenated into one 180 character long byte-like string, which is hard coded into the program obviously.
The bytes for each row are padded with a trailing zero because ABAP doesn't work with half a byte offsets - or at least I couldn't get it to work properly. I couldn't use a higher number base either (like base-64) due to how ABAP handles them and how complicated and lengthy it gets to do so. Also dealing with overflows. Meh.
DATA:                "Multiple declarations
  x(180) TYPE x,     "Byte-like string of length 180, holds the type weakness table
  v      TYPE x,     "One byte
  b, c.              "One letter chars for saving the values of bits (implicit char -> number conversion)

DEFINE g.  "Define a macro for reading two bits, we need this multiple times
  GET BIT: "Get bits from the hex encoded weakness table; index starts at 1
    &1 + 1 OF &2 INTO b, "get bit 1 ==> offset + 1
    &1 + 2 OF &2 INTO c. "get bit 2 ==> offset + 2

    SET BIT: "Unfortunately this is the best way of copying over bits (that I am aware of) to another value. Sets the two least significant bits so we get a value between 0 and 3.
     7 of v TO b, "Set result bit 1
     8 of v TO c. "Set result bit 2

    "This is the best I could come up with.
    IF v = 3. "If it's a binary 11 (3 in base10)
      o = 2.  "then it's double damage
    ELSE.  "For everything else the bit's base10 value can simply be halved, as 0/2=0, 1/2 =.5, 2/2=1.
      o = v / 2.
    ENDIF.
END-OF-DEFINITION. "End of macro bits

DEFINE f. "Define macro for finding the column/row of types in the weaknesses table both horizontally or vertically
  "Find first occurrence of the first three letters of the type and save the result to a newly initialized variable
  FIND &1(3) IN 'NorFigFlyPoiGroRocBugGhoSteFrrWatGraElePsyIceDraDarFai' RESULTS data(&2).
END-OF-DEFINITION. "End of macro types

FORM f USING a d e. "This subroutine has three parameters: [a]ttack, [d]efender primary, d[e]fender secondary
  DATA(o) ='1.00'. "Quick declaration of a number with 2 decimals

  "The type weakness table, encoded as binary, written in hexadecimal.
  "Each table row corresponds to 4.5 bytes = 36 bits, padded with half a byte of 0's => 40 bits = 5 bytes = 10 characters each
  x ='BAA8AAAAA0AE96AABA709A36A9EEA099E6A9BA90A99AAF2EA075EADFAAA09E7AB9AAA009A7AAAAE074D6799590AAF65DA690AAAA57E6A0AF6EB56EA0A6EA6A6AA09AAFAA9AE0BABAFAA6A0AAAA956FB0BAADAA8A709BA6EAA860'.

  f:a r,d s. "Call type index macro for the attack and primary defender type, saves the result in r (for attacker) and s (for defender)
  DATA(l) = r-offset / 3 * 2. "Find the attacker's "table column". Each column is 2 bits wide, but type abbreviations are 3 letters

  DATA(n) = o. "Result of the secondary defender's type weakness will be saved here; default (if no type is given) = 1x from o's default value

  IF strlen( e ) > 2. "When there was a secondary type given (length of type name > 2 characters)
    f e t. "Call type index macro for the secondary defender type.
    n = ( t-offset / 3 ) * 5. "Divide offset by 3 because abbreviations are 3 letters wide, then multiply by 5 to find the correct "table row" (5 bytes per row)
    g l x+n. "Call the bit reading macro; use attacker's column offset in bits and the defender's secondary type's row offset in bytes
    n = o.   "Save the resulting number to n temporarily
  ENDIF.

  o = ( s-offset / 3 ) * 5. "Get primary type's row offset in bytes
  g l x+o. "Call bit reading macro: attacker's column offset (bits), defender's primary row offset (bytes)
  o = o * n. "Multiply weakness by secondary weakness
  WRITE o.   "Print result
ENDFORM. "End of subroutine

Phew. How's that for some way too verbose code. There might be a better way to do it, but I doubt anyone else is mad enough to try golfing in ABAP. Still, suggestions are welcome, of course!

Python 2, 305 bytes
def f(l):d=[int(t[2:],36)%123%28%21for t in l];return reduce(lambda x,y:x*int("eJx1UIENwDAIekkM/982FW3skprUzA4oCHe6O6Jb9F05XXeATlDIouUndGLGRUYxTDD6o0YlhSmh/adnW7o8DzS6O9PWAmM9WnbLssktWgLjoNG1iwAw/RM4DqCIaZH3mv6l5MkF22fKKp6Vxtnji05OrsnADjaX+AByt1Y8".decode('base64').decode('zip')[d[0]*21:][y])/2.,d[1:],1)

Try it online!
Also hardcoding the whole matrix..

It seems like [d[0]*21+y] should work. Using #coding:L1 instead of base64 gets 253 bytes.

C (gcc), 382 363 bytes
I wanted to try something a little different, so I made a sparse array of the values that were not equal to 1 and encoded them in a base-90 format so that the column that they are on could be converted to +/-1.0 and added to a 1.0 multiplier.
Thanks to ceilingcat for the suggestion.
*y[]={"8(;","i56n9(q@wyD","j8o;t?","678:)tz","#ln9qr>u","4k7o;rw","456:;<tvyD","!pvC","n;<=?wz","8oq<=twB","mnr=>B","56mn9;<s>B","k%s>?B","jl;@1","km;<=tAx",";x2","4pvCD","j6;<xy"};i,j;float f(int**s){int k[3]={};float f=1;for(i=18;i--;)for(j=3;j--;s[j]&&!strcmp(z[i],s[j])?k[j]=i:0);for(char*t=y[*k];i=*t-33,*t++;)for(j=0;++j<3;)f*=i%18-k[j]?1:i/18/2.;return f;}

Try it online!

359 bytes