Files
BABA_YAGA/Assets/Scripts/Game/Elo_System_Spec.txt
2026-06-04 10:42:23 +07:00

98 lines
5.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Elo Rating System
AI Handoff Specification — Hallucinate Game
1. B?i c?nh & M?c tiêu
Game Hallucinate là game PvP online 1v1 (real-time), dùng Photon Fusion / Unity Relay làm backend m?ng. H? th?ng Elo ???c yêu c?u ?? x?p h?ng ng??i ch?i sau m?i tr?n solo, hi?n th? trên màn hình Profile và ?i?u ph?i matchmaking.
Yêu c?u c?t lõi:
• Tính toán Elo sau m?i tr?n 1v1 hoàn thành
• K-factor ??ng theo s? tr?n và rating hi?n t?i
• Persist rating lên server (không ?? client t? tính)
• Tr? v? rating m?i cho c? 2 ng??i ch?i sau tr?n
• Hi?n th? lên ProfileController.cs qua data binding
2. Công th?c Elo
2.1. Expected Score
E(A) = 1 / (1 + 10 ^ ((RatingB - RatingA) / 400))
E(B) = 1 - E(A)
2.2. Rating m?i
NewRating(A) = OldRating(A) + K * (Result - E(A))
Trong ?ó Result: Th?ng = 1.0 | Thua = 0.0 | Hòa = 0.5
2.3. K-Factor ??ng
?i?u ki?n
K Value
Lý do
D??i 30 tr?n (Placement)
40
Rating ch?a ?n ??nh, c?n h?i t? nhanh
Rating < 1200
32
Tier th?p — thay ??i nhi?u h?n
1200 ? Rating < 2000
24
Tier trung bình — cân b?ng
Rating ? 2000
16
Tier cao — ?n ??nh, thay ??i ch?m
2.4. Ví d? tính toán
A (1500) vs B (1200), A th?ng:
E(A) = 1 / (1 + 10^((1200-1500)/400)) = 0.849
E(B) = 0.151
NewRating(A) = 1500 + 24*(1 - 0.849) = 1500 + 3.6 ? 1504
NewRating(B) = 1200 + 32*(0 - 0.151) = 1200 - 4.8 ? 1195
3. Rank Tiers
Rank
Rating Range
Màu g?i ý (UI)
Iron
< 800
#8A8A8A
Bronze
800 999
#CD7F32
Silver
1000 1199
#C0C0C0
Gold
1200 1499
#FFD700
Platinum
1500 1799
#4DC8A0
Diamond
1800 2099
#7B6EE8
Master
? 2100
#E84D8A
Rating kh?i ??u (m?c ??nh): 1000. Rating sàn (floor): 100 — không xu?ng d??i giá tr? này.
4. Placement Matches
• 30 tr?n ??u tiên là Placement Period (gamesPlayed < 30)
• K = 40 trong giai ?o?n này ?? rating h?i t? nhanh v? ?úng v? trí
• Tùy ch?n: không hi?n th? rank badge trong 30 tr?n ??u, ch? hi?n th? '?' ho?c 'Unranked'
• Sau tr?n 30, K-factor chuy?n sang b?ng ??ng ? m?c 2.3
5. Ki?n trúc & Lu?ng d? li?u
5.1. Nguyên t?c quan tr?ng
KHÔNG ?? client t? tính Elo r?i g?i lên. Ph?i tính trên Host/Server ?? tránh cheat.
5.2. Lu?ng x? lý
B??c
Th?c hi?n b?i
Mô t?
1
Client A & B
Tr?n k?t thúc, g?i k?t qu? lên Host qua RPC
2
Host (Photon Fusion)
Nh?n k?t qu?, xác minh h?p l?
3
Host
G?i EloSystem.Calculate() v?i rating c?a 2 ng??i
4
Host ? Backend
G?i rating m?i lên server (UGS / Photon Cloud / custom)
5
Backend
Persist vào database, tr? v? k?t qu?
6
Host ? Client
Broadcast rating m?i v? cho c? 2 client qua RPC
7
Client
ProfileController c?p nh?t UI v?i rating m?i
5.3. File c?n t?o
File
V? trí
Trách nhi?m
EloSystem.cs
/Assets/Scripts/Game/
Pure static class — công th?c Elo, không dependency
EloData.cs
/Assets/Scripts/Game/
Struct/class l?u RatingResult (newRatingA, newRatingB, delta)
MatchResultRPC.cs
/Assets/Scripts/Network/
Photon RPC g?i/nh?n k?t qu? tr?n
ProfileController.cs
/Assets/Scripts/UI/
?ã có — thêm hàm UpdateEloDisplay(int newRating, int delta)
6. C# Implementation Spec
6.1. EloSystem.cs
public static class EloSystem {
public static EloResult Calculate(
int ratingA, int ratingB,
int gamesPlayedA, int gamesPlayedB,
float resultA) // 1=win, 0=lose, 0.5=draw
{
float eA = 1f / (1f + Mathf.Pow(10f, (ratingB - ratingA) / 400f));
int kA = GetK(ratingA, gamesPlayedA);
int kB = GetK(ratingB, gamesPlayedB);
int nA = Mathf.Max(100, Mathf.RoundToInt(ratingA + kA * (resultA - eA)));
int nB = Mathf.Max(100, Mathf.RoundToInt(ratingB + kB * ((1-resultA) - (1-eA))));
return new EloResult(nA, nB, nA - ratingA, nB - ratingB);
}
private static int GetK(int r, int g) =>
g < 30 ? 40 : r < 1200 ? 32 : r < 2000 ? 24 : 16;
}
6.2. EloResult struct
public readonly struct EloResult {
public int NewRatingA, NewRatingB, DeltaA, DeltaB;
// Constructor...
}
6.3. ProfileController — thêm hàm
public void UpdateEloDisplay(int newRating, int delta) {
root.Q<Label>("EloLabel").text = newRating.ToString();
root.Q<Label>("EloDelta").text = (delta >= 0 ? "+" : "") + delta;
root.Q<Label>("RankLabel").text = EloSystem.GetRank(newRating);
}
7. Tích h?p vào Profile UI
Các element UXML c?n có trong Profile.uxml:
Element Name
Type
Hi?n th?
EloLabel
Label
Rating hi?n t?i, VD: 1504
EloDelta
Label
Thay ??i sau tr?n, VD: +12 ho?c -8 (màu xanh/??)
RankLabel
Label
Tên rank, VD: Gold
WinRateBar
ProgressBar
value = winRate (0.0 - 1.0)
WinRateText
Label
VD: 58.3%
GamesPlayed
Label
T?ng s? tr?n
CSS class g?i ý cho EloDelta:
.elo-delta-positive { color: #4DC8A0; } /* teal — th?ng */
.elo-delta-negative { color: #E84D8A; } /* h?ng — thua */
8. Checklist tri?n khai
#
Task
File liên quan
Done?
1
T?o EloSystem.cs v?i Calculate() và GetRank()
EloSystem.cs
[ ]
2
T?o EloResult struct
EloData.cs
[ ]
3
Thêm RPC g?i k?t qu? tr?n lên Host
MatchResultRPC.cs
[ ]
4
Host g?i EloSystem.Calculate() sau khi nh?n RPC
MatchResultRPC.cs
[ ]
5
Persist rating lên backend
Backend / Cloud Save
[ ]
6
Host broadcast EloResult v? client qua RPC
MatchResultRPC.cs
[ ]
7
ProfileController.UpdateEloDisplay() nh?n data m?i
ProfileController.cs
[ ]
8
Thêm các UXML element vào Profile.uxml
Profile.uxml
[ ]
9
Style EloDelta (xanh/??) trong Global.uss
Global.uss
[ ]
10
Unit test EloSystem v?i các tr??ng h?p biên
Test/
[ ]
9. L?u ý & Edge Cases
• Tr?n b? disconnect gi?a ch?ng: không tính Elo, ho?c tính thua cho bên disconnect (quy?t ??nh tùy game design)
• Tr?n quá ng?n (< X giây): có th? b? qua ?? tránh farming/griefing
• Rating floor = 100: Mathf.Max(100, newRating) — ?ã có trong spec
• Không có rating cap (ceiling) — Master+ ti?p t?c t?ng vô h?n
• Hòa (draw): resultA = 0.5f — game c?n quy?t ??nh có h? tr? không
• Backend sync: l?u c? gamesPlayed cùng v?i rating ?? K-factor tính ?úng