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


Posted 2018-07-07T20:48:59.757

Reputation: 55 648

Can the inputs require leading zeroes? I.e. 04k – Amphibological – 2018-07-07T21:04:11.350

6No; 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 – 2018-07-07T21:10:50.483

2Are we allowed to take the inputs as (int, string) pairs? – None – 2018-07-07T21:15:51.727

9No; 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 – 2018-07-07T21:26:06.153



JavaScript (ES7), 58 bytes

Takes the two strings in currying syntax (a)(b).


Try it online!


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.


Posted 2018-07-07T20:48:59.757

Reputation: 111 334

Not as clever, but -3: a=>b=>(g=s=>parseInt(s)*~{d:~1,p:~8}[s[1]])(a)>g(b)?a:b – FireFly – 2018-07-15T10:12:17.000

2@FireFly Thanks. But a similar answer was already submitted and I'd better let this one unchanged. – Arnauld – 2018-07-15T10:22:29.270


Jelly, 14 bytes


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.


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

Jonathan Allan

Posted 2018-07-07T20:48:59.757

Reputation: 67 804

Very nice method! – Arnauld – 2018-07-08T16:19:02.133

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 – 2018-07-10T16:45:09.720

@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 – 2018-07-10T16:50:53.953

1@JonathanAllan Ah, I see, thank you! – Post Self – 2018-07-10T17:18:23.840


Jelly,  11  10 bytes

Inspired to go back to the drawing board by Arnauld!


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.


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)

Jonathan Allan

Posted 2018-07-07T20:48:59.757

Reputation: 67 804

I knew it had to happen. :) But I'm satisfied anyway to have outgolfed you for about 90 minutes. :p – Arnauld – 2018-07-09T09:09:44.670

I still feel a little silly for not having explored an avenue which came to my mind right at the start! – Jonathan Allan – 2018-07-09T09:11:00.773

This is a really neat answer! – Lynn – 2018-07-11T01:40:06.857


MATL, 30 28 25 23 bytes


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)


  • 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


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

sundar - Reinstate Monica

Posted 2018-07-07T20:48:59.757

Reputation: 5 296

1So now we have Arnauld's algorithm and the sundar polynomial method. Lovely. +1 – David Conrad – 2018-07-08T01:12:20.280


Haskell, 73 71 bytes

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)


Posted 2018-07-07T20:48:59.757

Reputation: 411

You can save 1 byte by changing True to 1<2. – Amphibological – 2018-07-08T22:14:16.977

1(-x) can be 0-x. – Lynn – 2018-07-08T22:51:21.890

you could change the final pattern match to r(x,_)=x+7, too! – Lynn – 2018-07-09T17:39:56.193

You forgot to change the final pattern match, it should be 69 (also 0-x can be -x) – ASCII-only – 2019-04-03T11:24:16.160


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!


Posted 2018-07-07T20:48:59.757

Reputation: 115 687


R, 73 bytes


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


Posted 2018-07-07T20:48:59.757

Reputation: 4 599

I tried using L in place of . but it's not working either... will let you know if I can find something else. – JayCe – 2018-07-10T21:00:14.437

Congrats on 1K! – Giuseppe – 2018-08-01T18:43:22.923


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.


Try it online!


“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
                   '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'


Posted 2018-07-07T20:48:59.757

Reputation: 111 334

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 – 2018-07-09T08:43:28.993

...now I have 11 – Jonathan Allan – 2018-07-09T08:58:36.587


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)


Very similar code works in 0.6 Try it online


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'))

julia> !(Int('d'))

julia> !(Int('p'))

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"
Test Summary: | Pass  Total
Check it      |   14     14
Test.DefaultTestSet("Check it", Any[], 14, false)

Lyndon White

Posted 2018-07-07T20:48:59.757

Reputation: 1 021

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 - Reinstate Monica – 2018-07-13T08:52:23.970

Nice, and then i can ise Int[r...] rather than Int([r...]) to save a few more. Thanks – Lyndon White – 2018-07-13T13:27:07.010

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 - Reinstate Monica – 2018-07-13T14:03:20.337

Thanks, my other recent julia 0.7 answers suffered much more from avoiding depredations than this one did. – Lyndon White – 2018-07-13T14:10:11.100

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 - Reinstate Monica – 2018-07-20T12:48:47.857

Hmmm, no seems not. Maybe I was thinking of an earlier version of this answer. Or possibly even a 0.5->0.6 deprecation – Lyndon White – 2018-07-20T13:59:02.060


Haskell, 64 bytes

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)]

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


Posted 2018-07-07T20:48:59.757

Reputation: 115 687


MATL, 28 27 bytes

,w'kdp'X{'*-1 . *8'YbYXU]>G

Try it online! Or verify all test cases.


,                % 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)

Luis Mendo

Posted 2018-07-07T20:48:59.757

Reputation: 87 464


Jelly, 16 bytes

Takes input as a list of two strings.


Try it online! (all test cases)


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


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$$


Try it online!


Posted 2018-07-07T20:48:59.757

Reputation: 111 334


JavaScript (ES6), 55 54 bytes

-1 byte thanks to @Shaggy


Try it Online!


  (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

Herman L

Posted 2018-07-07T20:48:59.757

Reputation: 3 611

This seems to work for 54. – Shaggy – 2018-07-08T13:42:33.810

@Shaggy your solution fails for f("2d")("1d"), but replacing d:1 with d:2 fixed it. – Herman L – 2018-07-08T14:06:27.227


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

– tsh – 2018-07-09T08:51:30.343


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.

Silvio Mayolo

Posted 2018-07-07T20:48:59.757

Reputation: 1 817


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 – 2018-07-11T15:38:14.240


Ruby, 52 46 bytes


Straightforward port of xnor's answer.

Try it online!

EDIT: -6 bytes by realizing I could have used a Proc


Posted 2018-07-07T20:48:59.757

Reputation: 1 394

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 – 2018-07-08T21:06:03.307


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


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.



Posted 2018-07-07T20:48:59.757

Reputation: 424

1You can save a byte with currying i.e. a=>b=>. – TheLethalCoder – 2018-07-11T11:40:59.597

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 – 2018-07-11T11:47:36.783

@TheLethalCoder: PadLeft requires char and using 107 instead of 'k' makes no difference. – raznagul – 2018-07-11T11:52:14.597

Oh I thought the conversion was implicit... worth a try though – TheLethalCoder – 2018-07-11T11:52:53.400


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


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

Kevin Cruijssen

Posted 2018-07-07T20:48:59.757

Reputation: 67 575

1122 bytes – Sara J – 2019-04-02T20:36:59.877

@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 – 2019-04-02T21:23:19.517


R, 65 62 bytes


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:

46 Levels: 30k < 29k < 28k < 27k < 26k < 25k < 24k < 23k < 22k < ... < 9p


Posted 2018-07-07T20:48:59.757

Reputation: 21 077

162 bytes with ordered(...) instead of factor(...,o=T) – Robin Ryder – 2019-04-02T13:46:36.207

@RobinRyder thanks! I read through the docs page for factor and completely missed out on ordered. – Giuseppe – 2019-04-02T13:51:14.467


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

Sara J

Posted 2018-07-07T20:48:59.757

Reputation: 2 576

1(read$init k) can be read(init k). An auxiliary function is shorter than using case of: Try it online! – Laikoni – 2019-04-03T06:01:01.373

@Laikoni Thanks! – Sara J – 2019-04-03T10:28:36.943

168? – ASCII-only – 2019-04-03T10:52:15.043

@ASCII-only Nice, thanks! – Sara J – 2019-04-03T12:29:08.340

1@SaraJ you haven't updated the link :P – ASCII-only – 2019-04-03T12:37:57.633

sadly returning a tuple doesn't work because you need the * to negate for k – ASCII-only – 2019-04-03T23:55:24.683

and something like fromMaybe -1$elemIndex" d p" is too long :| – ASCII-only – 2019-04-04T00:02:39.580

My solution is 66 bytes and very similar :) keep golfing down that expression! – Lynn – 2019-04-06T21:22:42.243


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!

Mr. Xcoder

Posted 2018-07-07T20:48:59.757

Reputation: 39 774


Julia 0.6, 75 71 65 bytes


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))

sundar - Reinstate Monica

Posted 2018-07-07T20:48:59.757

Reputation: 5 296


Retina 0.8.2, 29 bytes


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


Sort the lines...


... using the specified key...


... in reverse order.


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.


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


Posted 2018-07-07T20:48:59.757

Reputation: 95 035


J, 39 bytes


Try it online!


[`]@.(<&(".@}: ((* _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


Posted 2018-07-07T20:48:59.757

Reputation: 8 729


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!

Jonathan Allan

Posted 2018-07-07T20:48:59.757

Reputation: 67 804


C (gcc), 95 bytes

#define _(s)(strtol(s,&u,10)+(*u^15)*8)*(-(*u&2)+1)

Try it online!


Posted 2018-07-07T20:48:59.757

Reputation: 2 149

Good use of old-style function declarations. – SIGSTACKFAULT – 2018-07-11T16:48:34.043


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.

Phil H

Posted 2018-07-07T20:48:59.757

Reputation: 1 376


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>


php -n go_rank.php 1p 7d

Or Try it online!

PHP (7.4), 74 bytes


Try it online!


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!


Posted 2018-07-07T20:48:59.757

Reputation: 5 484


05AB1E, 12 bytes


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


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

Kevin Cruijssen

Posted 2018-07-07T20:48:59.757

Reputation: 67 575


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.


Test cases here: Try it online!

Old 61 bytes answer


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

Posted 2018-07-07T20:48:59.757

Reputation: 868

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

– Kevin Cruijssen – 2019-04-01T13:52:11.357

Also, doesn't Scala have lambdas as well to golf def f(a:String,b:String)= to a=>b=>? – Kevin Cruijssen – 2019-04-01T13:55:41.977

@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 – 2019-04-01T13:57:02.330

2@V.Courtois you can add typing in the header – ASCII-only – 2019-04-01T14:03:44.987


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

– Kevin Cruijssen – 2019-04-01T14:07:20.947

2203 – ASCII-only – 2019-04-01T14:08:12.013

1188 bytes by returning a variable. – Kevin Cruijssen – 2019-04-01T14:12:16.330

286 – ASCII-only – 2019-04-01T14:16:55.830

@ASCII-only Yeah, that might be a better approach. :D Whopping -102 bytes right there. – Kevin Cruijssen – 2019-04-01T14:18:18.037

264 – ASCII-only – 2019-04-01T14:20:48.987

162 – ASCII-only – 2019-04-01T14:35:37.847

161 – ASCII-only – 2019-04-01T14:45:03.497

56 – ASCII-only – 2019-04-03T01:55:04.113

Sadly that 56 doesn't work on test case 3 (6d,1p should return 1p). – V. Courtois – 2019-04-03T09:25:08.300

54? – ASCII-only – 2019-04-04T00:03:42.320

how :o nice one – V. Courtois – 2019-04-05T06:42:50.853


[R], 87 [bytes]


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]

Zahiro Mor

Posted 2018-07-07T20:48:59.757

Reputation: 371


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

– digEmAll – 2018-07-08T17:19:03.097

1nice! evaluating the inequality..... I did not think of that :) – Zahiro Mor – 2018-07-09T11:29:44.543

You can update your answer based on @digEmAll's comment - nice approach! – JayCe – 2018-08-17T00:53:38.717


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

– JayCe – 2018-08-17T01:56:51.950


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)"                            ''  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

Taylor Scott

Posted 2018-07-07T20:48:59.757

Reputation: 6 709


R, 83 bytes


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!


Posted 2018-07-07T20:48:59.757

Reputation: 2 655


Jelly, 20 bytes


Try it online!

Erik the Outgolfer

Posted 2018-07-07T20:48:59.757

Reputation: 38 134


Pyth, 15 bytes

Also uses Arnauld's method.


Try it here!

Mr. Xcoder

Posted 2018-07-07T20:48:59.757

Reputation: 39 774


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
else f
else if(n[2]=='k')f
else s
else f
else f}

Try it online!


Posted 2018-07-07T20:48:59.757

Reputation: 611

46 – ASCII-only – 2019-04-03T02:03:42.900