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

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 : 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 – Amphibological Jul 7 '18 at 21:04
• 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.) – Lynn Jul 7 '18 at 21:10
• Are we allowed to take the inputs as (int, string) pairs? – user48543 Jul 7 '18 at 21:15
• 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. – Lynn Jul 7 '18 at 21:26

# 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 Jul 15 '18 at 10:12
• @FireFly Thanks. But a similar answer was already submitted and I'd better let this one unchanged. – Arnauld Jul 15 '18 at 10:22

# 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 – Arnauld Jul 9 '18 at 9:09
• I still feel a little silly for not having explored an avenue which came to my mind right at the start! – Jonathan Allan Jul 9 '18 at 9:11
• This is a really neat answer! – Lynn Jul 11 '18 at 1:40

# 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! – Arnauld Jul 8 '18 at 16:19
• 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? – Post Self Jul 10 '18 at 16:45
• @PostSelf the bytecode is 14 bytes, the unicode characters just represent single bytes - see the codepage in the link to "bytes" in the header. – Jonathan Allan Jul 10 '18 at 16:50
• @JonathanAllan Ah, I see, thank you! – Post Self Jul 10 '18 at 17:18

# MATL, 302825 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 – David Conrad Jul 8 '18 at 1:12

r(x,"k")=0-x
r(x,"d")=x
r(x,"p")=x+7
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. – Amphibological Jul 8 '18 at 22:14
• (-x) can be 0-x. – Lynn Jul 8 '18 at 22:51
• you could change the final pattern match to r(x,_)=x+7, too! – Lynn Jul 9 '18 at 17:39
• You forgot to change the final pattern match, it should be 69 (also 0-x can be  -x) – ASCII-only Apr 3 '19 at 11:24

# 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. – JayCe Jul 10 '18 at 21:00
• Congrats on 1K! – Giuseppe Aug 1 '18 at 18:43

# 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! – Jonathan Allan Jul 9 '18 at 8:43
• ...now I have 11 – Jonathan Allan Jul 9 '18 at 8:58

# 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. – sundar - Remember Monica Jul 13 '18 at 8:52
• Nice, and then i can ise Int[r...] rather than Int([r...]) to save a few more. Thanks – Lyndon White Jul 13 '18 at 13:27
• 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. – sundar - Remember Monica Jul 13 '18 at 14:03
• Thanks, my other recent julia 0.7 answers suffered much more from avoiding depredations than this one did. – Lyndon White Jul 13 '18 at 14:10
• 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. – sundar - Remember Monica Jul 20 '18 at 12:48

r[(n,[c])]=n*(gcd(fromEnum c)28-3)
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


### 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 Jul 8 '18 at 13:42
• @Shaggy your solution fails for f("2d")("1d"), but replacing d:1 with d:2 fixed it. – Herman L Jul 8 '18 at 14:06
• Try to golf this, but still 54 bytes... so sad – tsh Jul 9 '18 at 8:51
• 53 – l4m2 Jun 16 '20 at 8:10
• but seems quite different, join or sep? – l4m2 Jun 16 '20 at 8:14

# Ruby, 52 46 bytes

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


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 – Conor O'Brien Jul 8 '18 at 21:06

# 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 =>
{
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=>. – TheLethalCoder Jul 11 '18 at 11:40
• 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 Jul 11 '18 at 11:47
• @TheLethalCoder: PadLeft requires char and using 107 instead of 'k' makes no difference. – raznagul Jul 11 '18 at 11:52
• Oh I thought the conversion was implicit... worth a try though – TheLethalCoder Jul 11 '18 at 11:52

# 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! – Dom Hastings Jul 11 '18 at 15:38

# 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) – Robin Ryder Apr 2 '19 at 13:46
• @RobinRyder thanks! I read through the docs page for factor and completely missed out on ordered. – Giuseppe Apr 2 '19 at 13:51

# Java 8, 128122 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 – Sara J Apr 2 '19 at 20:36
• @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.. – Kevin Cruijssen Apr 2 '19 at 21:23

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! – Laikoni Apr 3 '19 at 6:01 • @Laikoni Thanks! – Sara J Apr 3 '19 at 10:28 • 68? – ASCII-only Apr 3 '19 at 10:52 • @ASCII-only Nice, thanks! – Sara J Apr 3 '19 at 12:29 • @SaraJ you haven't updated the link :P – ASCII-only Apr 3 '19 at 12:37 # 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, 7571 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! # 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.

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, 30761 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!

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!

• @V.Courtois you can add typing in the header – ASCII-only Apr 1 '19 at 14:03
• As @ASCII-only mentioned, you can create it in the header with a var f: Function1[String, Function1[String, String]]=. 247 bytes. – Kevin Cruijssen Apr 1 '19 at 14:07
• 203 – ASCII-only Apr 1 '19 at 14:08
• 86 – ASCII-only Apr 1 '19 at 14:16
• 64 – ASCII-only Apr 1 '19 at 14:20

# 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


# 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!

# 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. – SIGSTACKFAULT Jul 11 '18 at 16:48

# 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