# Introduction

This time, I added __Rating function __ to the Tenhou automatic grade management app created in the following post.

# Rating formula

Rating adopts the following Tenhou format. In Tenhou, the Rating is also a complete ranking system based on the complete ranking system of grades. In our private room, we adopted the rule that the weight of the ranking point is relatively high, so we adopted the Tenhou style Rating.

(Partial excerpt below)

--The ranking points (10-30) are corrected by the average of the same table players. --Initial value = R1500 --The higher the average R of the table, the greater the increase. (Rate fluctuation) = (Number of games corrected) x (Competition result + Correction value) x (Scaling coefficient) Match number correction (less than 400 matches): 1-Number of matches x 0.002 Match number correction (400 games or more): 0.2 Match result (3 dan battle): 1st place +30 2nd place 0 3rd place-30 Correction value: (Table average R-your own R) / 40 Scaling factor (dan battle): 1.0

If your private room has a rule that emphasizes raw scores over ranking scores, it may be more correlated with grades if you emphasize other Rating formulas. The following site calculates the Rating of M Leaguer using three types of formulas, so please refer to it.

# Implementation

## Preamble

``````import re
import matplotlib.pyplot as plt
``````

## Rating / R initialization

The rating data is initialized by the initial data (Player name: player, initial R: init_rating, initial number of villas: init_games) in ʻinitial_file.txt`. Basically R = 1500, games = 0.

#### `calc_rating.py`

``````

def initialize_rating(initial_file):

with open(initial_file) as f:

rating = {}
games  = {}
rating_history = {}

for l in init_ratings:
player      = l.split()[0]
init_rating = l.split()[1]
init_games  = l.split()[2]
rating[player] = float(init_rating)
games[player]  = int(init_games)
rating_history[player] = [float(init_rating)]
print(games)

return rating,games,rating_history
``````

#### `initial_file.txt`

``````
Mr. A 1500 0
Mr. B 1500 0
Mr. C 1500 0
Mr. D 1500 0
``````

## Calculation of R

• Obtain the R fluctuation value from the battle result in the log file, and add the R fluctuation value to the initial R.
• The following 5 arguments
• Initial R
• Initial number of games
• R history up to that point
• Log file path
• With or without tip
• The following three return values
• Final R
• Last number of games
• R history
• For example, if there are multiple log files, the total R can be calculated by executing this function multiple times.
``````def calc_rating(initial_rating,initial_games,initial_rating_history,logfile,tip=False):
with open(logfile) as f:
lines = f.readlines() #Read everything up to the end of the file line by line(Newline character is also included)

rating = initial_rating
games  = initial_games
rating_history = initial_rating_history

for line in lines[1:]:
# print(games)

if len(line) > 10: #Skip strange lines

roomid  = line.split("|")[0]
time    = line.split("|")[1]
rools   = line.split("|")[2]
players = line.split("|")[3]

#Without congratulations
if tip == False:
l = re.split('[ ()]', players)
player1 = l[1]
player2 = l[4]
player3 = l[7]

#If there is a celebration
if tip == True:
l = re.split('[ (,)]', players)
player1 = l[1]
player2 = l[5]
player3 = l[9]

rate_average = (rating[player1]+rating[player2]+rating[player3])/3.0
rate_average = round(rate_average,3)
if rate_average < 1500.0:
rate_average = 1500.0

#Match number correction
if games[player1] < 400:
games_correction1 = 1.0 - games[player1]*0.002
if games[player1] >= 400:
games_correction1 = 0.2
if games[player2] < 400:
games_correction2 = 1.0 - games[player2]*0.002
if games[player2] >= 400:
games_correction2 = 0.2
if games[player3] < 400:
games_correction3 = 1.0 - games[player3]*0.002
if games[player3] >= 400:
games_correction3 = 0.2

#Average R correction
averageR_correction1 = (rate_average - rating[player1])/40.0
averageR_correction2 = (rate_average - rating[player2])/40.0
averageR_correction3 = (rate_average - rating[player3])/40.0

#Rating fluctuation
rate_delta1 = round(games_correction1 * ( 30.0  + averageR_correction1 ) * 1.0, 3) # 1st
rate_delta2 = round(games_correction2 * ( 0.0   + averageR_correction2 ) * 1.0, 3) # 2nd
rate_delta3 = round(games_correction3 * ( -30.0 + averageR_correction3 ) * 1.0, 3) # 3rd
print(rate_delta1,rate_delta2,rate_delta3)

# Rating
rating[player1] += rate_delta1
rating[player2] += rate_delta2
rating[player3] += rate_delta3

# Rating History
rating_history[player1].append(rating[player1])
rating_history[player2].append(rating[player2])
rating_history[player3].append(rating[player3])

# Games
games[player1] += 1
games[player2] += 1
games[player3] += 1

return rating,games,rating_history
``````

## Graphing R fluctuations

• A polygonal line (plot), a one-point plot of the final R (scatter), and the final R value are displayed.
``````def rating_plot(rating_history):

plt.clf()
names = {"Mr. A":"a","Mr. B":"b","Mr. C":"c","Mr. D":"d"}
for player in rating_history.keys():
x = [i for i in range(len(rating_history[player]))]
y = rating_history[player]
plt.plot(x,y,linewidth=0.5,alpha=0.5)
plt.scatter(x[-1],y[-1],label=names[player])
plt.text(x[-1],y[-1]+5,int(y[-1]))

plt.legend()
plt.savefig("rating.png ")
``````

## Run (test in local environment)

``````if __name__ == "__main__":
r,g,h = initialize_rating("rating.txt")
r,g,h = calc_rating(r,g,h,"logvol1.txt",tip=False)
r,g,h = calc_rating(r,g,h,"logvol2.txt",tip=True)
r,g,h = calc_rating(r,g,h,"logvol3.txt",tip=True)

rating_plot(h)
``````

## Output result

• I'm playing mahjong with 9 people, so 9 graphs are displayed.
• If the average R is 1500 or less, it is rounded up, so the average is larger than 1500.
• As the number of games increases, the fluctuation of R becomes smaller (it does not change after the 400th game).

## Implementation on LINE bot

• Put the previous program in the `rating` folder and call it from the line bot program.
• Calculates the rating and sends the graph when there was a `postback` action in the continuation of the last time.
• It seems that if you keep the rating calculated once, you do not have to calculate it every time, but since it seems that you will use DB etc., I decided to calculate it every time for the time being.
• Since there is a log file until Season 3, the total R is calculated.
• Now you can always see the latest (up to the previous day) R from LINE every day.

#### `tenhoulinebot.py`

``````

(Omission)
import rating.calc_rating as cr
(Omission)
elif postbackdata == "request_rating":

initial_rating,initial_games,initial_rating_history = cr.initialize_rating("rating/rating.txt")
r,g,h = cr.calc_rating(initial_rating,initial_games,initial_rating_history,"rating/logvol1.txt",tip=False)
r,g,h = cr.calc_rating(r,g,h,"rating/logvol2.txt",tip=True)
r,g,h = cr.calc_rating(r,g,h,"rating/logvol3.txt",tip=True)
cr.rating_plot(h)

s3_image_url = s3_client.generate_presigned_url(
ClientMethod = 'get_object',
Params       = {'Bucket': aws_s3_bucket, 'Key': "rating.png "},
ExpiresIn    = 600,
HttpMethod   = 'GET'
)