6
\$\begingroup\$

I'm new to Japanese Mahjong and have trouble calculating the scores. Please help me write a program to calculate it.

Introduction

(aka you can skip this boring part)

Mahjong is a popular tile game in Asia. Different rules have evolved and systematically established in various regions. Japan, being an isolated island state, have its own mahjong variant including rules and scoring.

In Japanese Mahjong, 4 players participate in the game at the same time. They take turn to draw a tile from wall and discard a tile from hand. Occasionally then can take other's discarded tiles for use. Each round 1 of the 4 players will be the dealer. Independent of that, each round may have a winner if he/she can assemble a winning hand. There could be no winners in a round but we don't care about that in this challenge.

When a player become the winner of that round, the change in score is calculated depending on 4 things: (1) Han and (2) fu depends on the winning hand, (3) whether the winner is also a dealer, and (4) whether he win by self-draw or from other's discard. The winner get his points by taking the points from the losers. So points lost by all losers added should equal to points gained by winner. Moreover, sometimes only one loser lose his points while the other two don't lose any, and sometimes one of the three losers lose more points. (See below)

Objective

Write a program or function that accepts the 4 parameters mentioned above, and return (1) how many points the winner will gain, and (2-4) how many points each of the three losers will lose.

Details of inputs

Han: An integer from 1 to 13 inclusive.
Fu: An integer that is either 25, or an even number from 20 to 130 inclusive. If han is 1 or 2, fu is further capped at 110.
dealer or not: A truthy/falsy value. Though if you are a mahjong lover you can instead accept the winner's seat-wind :-)
self-draw or discard: A two-state value. Truthy/falsy value will suffice but other reasonable values are acceptable.

Steps of calculating the points gained/lost:

1. Calculate the base score

The base score is calculated based on 2 cases.

1a) Han ≤ 4

If fu is 25, then keep it as-is. Otherwise round it up to nearest 10.

Then base score is calculated by
base_score = fu×2han+2
And it's capped at 2000 points.

1b) Han ≥ 5

Fu is not used here. The base score is obtained by direct lookup of han in the following table:

Han    Base score
5      2000
6-7    3000
8-10   4000
11-12  6000
13     8000

2. Calculate the points lost by losers

2a) Dealer wins by self-draw
Each loser pays an amount equal to double of the base score, then rounded up to nearest 100.

2b) Dealer wins by discard
The discarding loser pays an amount equal to 6x the base score, then rounded up to nearest 100. Other two losers don't pay.

2c) Non-dealer wins by self-draw
Dealer (one of the loser) pays an amount equal to double of the base score, then rounded up to nearest 100. The other two losers each pays an amount equal to base score, also rounded up to 100.

2d) Non-dealer wins by discard
The discarding loser pays an amount equal to 4x the base score, then rounded up to 100. Other two losers don't need to pay. Scoring isn't affected by whether the discarding loser is dealer or not.

3. Calculate the points gained by winner

Winner gains the total points lost by the three losers. Not a point less and not a point more.

Example

(Some examples are shamelessly taken/modified from Japanese Wikipedia page of calculating mahjong score)

4 han 24 fu, dealer win by self-draw

24 fu rounded up to 30 fu
Base score = 30 × 2 4+2 = 1920
Each loser pays 1920×2 = 3840 => 3900 (rounded)
Winner takes 3900×3 = 11700 points
Output: +11700/-3900/-3900/-3900

3 han 25 fu, dealer win by discard

25 fu no need to round up
Base score = 25 × 2 3+2 = 800
Discarding loser pays 800×6 = 4800
Non-discarding loser pays 0 points
Winner takes 4800 points
Output: +4800/-4800/0/0

4 han 40 fu, non-dealer win by self-draw

40 fu no need to round up
Base score = 40 × 2 4+2 = 2560 => 2000 (capped)
Dealer pays 2000×2 = 4000
Non-dealer pays 2000
Winner takes 4000+2000×2 = 8000 points
Output: +8000/-4000/-2000/-2000

9 han 50 fu, non-dealer win by discard

Fu not used to calculate score
Base score = 4000 (table lookup)
Discarding loser pays 4000×4 = 16000
Non-discarding loser pays 0 points
Winner takes 16000 points
Output: +16000/-16000/0/0

Output

Output 4 numbers: The point that the winner is gaining (as positive number), and the points that each of the three losers is losing (as negative number).
You may output the numbers as 4 individual numerical values in your language's native format, an array/list of numbers, 4 separate strings (or array/list of characters), or a string contatining the numbers that are properly delimited. If you use string, the positive sign is optional but negative sign is mandatory.
The order of the numbers doesn't matter, since they can be inferred from sign and magnitude of the numbers. e.g. 0/-16000/+16000/0 is acceptable.

Format

  • Full program or function
  • Any reasonable input/output format
  • Default rules apply. Standard loopholes forbidden. so fewest bytes wins.

Notes

  • All outputs are guaranteed to be integers, more specifically multiples of 100 in the range [-48000,+48000]
  • Rounding is applied only to (1) fu and (2) loser's points. It is not applied to base score.
  • Theoretically winning by self-draw or discard should gain the same points (6x or 4x base points). However due to rounding, this may not be true at low han. (See 1st and 2nd test cases)

Test cases

(Also see example)

Inputs                         Outputs
Han    Fu     Dealer  Self     Winner   Loser1    Loser2    Loser3
1      22     N       Y        +1100    -500      -300      -300
1      22     N       N        +1000    -1000     0         0
3      106    Y       N        +12000   -12000    0         0     
4      20     N       Y        +5200    -2600     -1300     -1300
4      25     Y       N        +9600    -9600     0         0
5      88     Y       Y        +12000   -4000     -4000     -4000
7      25     Y       Y        +18000   -6000     -6000     -6000
12     64     N       N        +24000   -24000    0         0
13     130    Y       N        +48000   -48000    0         0
\$\endgroup\$
  • \$\begingroup\$ Just spent a few hours for a C#6 answer but it's over 200 bytes. I will further golf it and post it after others have a chance to. \$\endgroup\$ – Link Ng Nov 22 '16 at 6:04
3
\$\begingroup\$

05AB1E, 79 bytes

05AB1E uses CP-1252 encoding.

Input order:

Han
Fu
Dealer = 1, Non-dealer = 0
Self = 2, Discard = 0

Code:

3‹i6b‚ï{0è}¹5‹iÐT/îT*‚sÈè¹Ìo*3°·‚{0èë\•j¤È°•¹è3°*}U•é2TˆÞ•S3ôŠ+èX*Tn/îTn*(DOĸì

Try it online!

Explanation to come.

\$\endgroup\$
3
\$\begingroup\$

Python 2, 128 bytes

def m(h,f,o,t):C=100;b=[24/(50/h)*1000,min(2000,[-f/10*~9,f][f%2]<<h+2)][h<5];x=(o|2-t)*-2*b/C*C;y=~o*t*b/C*C;return-x-y-y,x,y,y

h is han, f is fu, o is dealer (1/0), t is self-draw (1/0). Outputs in the same order as the test cases. ideone link.

Expanded slightly:

def m(h,f,o,t):
  C = 100

  # Base score
  b = [24/(50/h)*1000, min(2000,[-f/10*~9,f][f%2]<<h+2)][h<5]

  # Player scores - uses the -x/100*-100 approach to round up
  x = (o|2-t)*-2*b/C*C
  y = ~o*t*b/C*C

  return -x-y-y, x, y, y

Indirect thanks to @MartinEnder.

\$\endgroup\$
0
\$\begingroup\$

C#6, 211 bytes

using static System.Math;(h,f,x,y)=>{int b=h>5?24/(50/h)*100:Min(f%2<1?(int)Ceiling(f*.1)<<h+2:10<<h,200),c=-(int)Ceiling(b*.1*(y?2:x?6:4))*100,d=-(int)Ceiling(b*.1*(y?x?2:1:0))*100;return new[]{-c-d*2,c,d,d};};

Contains an idea shamelessly stolen from @Sp3000.

Assign to Func<int,int,bool,bool,int[]>
h=han
f=fu
x=true if dealer wins, false if non-dealer wins
y=true if self draw, false if discard
returns int[], [0]=winner, [1]-[3]:loser
repl.it demo

Ungolfed + explanations

// b is base score / 10
int b=h>5
    // han is [6,13], use Sp3000's idea
    ?24/(50/h)*100
    // han <= 5, calculate base score (/ 10) from formula...
    // Yes this formula works for han==5 also
    :Min(
        f%2<1
            // round up to 10, then times 2^(h+2)
            ?(int)Ceiling(f*.1)<<h+2
            // b=2.5*2^(h+2)=10*2^h=10<<h
            :10<<h
        // Cap at 200, thus base score is capped at 2000
        ,200),
// Multiply 0.1b by 2/4/6 according to input, then multiply by 100 to get actual score
c=-(int)Ceiling(b*.1*(y?2:x?6:4))*100,
d=-(int)Ceiling(b*.1*(y?x?2:1:0))*100;
// Array type implied from arguments
return new[]{-c-d*2,c,d,d};
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.