Skip to content

Commit

Permalink
Merge pull request #394 from sparcs-kaist/#393-events-bug-fix
Browse files Browse the repository at this point in the history
#393 테스트 과정 중 발생한 버그 수정
  • Loading branch information
kmc7468 authored Sep 24, 2023
2 parents aa7ca0b + 560b174 commit 5017281
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 14 deletions.
33 changes: 32 additions & 1 deletion src/lottery/routes/docs/publicNotice.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = {
"application/json": {
schema: {
type: "object",
required: ["leaderboard"],
required: [
"leaderboard",
"totalTicket1Amount",
"totalTicket2Amount",
"totalUserAmount",
],
properties: {
leaderboard: {
type: "array",
Expand All @@ -59,6 +64,7 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = {
"ticket1Amount",
"ticket2Amount",
"probability",
"probabilityV2",
],
properties: {
nickname: {
Expand Down Expand Up @@ -86,9 +92,29 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = {
description: "1등 당첨 확률",
example: 0.001,
},
probabilityV2: {
type: "number",
description: "근사적인 상품 당첨 확률",
example: 0.015,
},
},
},
},
totalTicket1Amount: {
type: "number",
description: "전체 일반 티켓의 수",
example: 300,
},
totalTicket2Amount: {
type: "number",
description: "전체 고급 티켓의 수",
example: 100,
},
totalUserAmount: {
type: "number",
description: "리더보드에 포함된 유저의 수",
example: 100,
},
rank: {
type: "number",
description: "유저의 리더보드 순위. 1부터 시작합니다.",
Expand All @@ -99,6 +125,11 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = {
description: "1등 당첨 확률",
example: 0.00003,
},
probabilityV2: {
type: "number",
description: "근사적인 상품 당첨 확률",
example: 0.00045,
},
},
},
},
Expand Down
1 change: 1 addition & 0 deletions src/lottery/services/globalState.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const createUserGlobalStateHandler = async (req, res) => {

eventStatus = new eventStatusModel({
userId: req.userOid,
creditAmount: 100, // 초기 송편 개수는 0개가 아닌 100개입니다.
});
await eventStatus.save();

Expand Down
87 changes: 74 additions & 13 deletions src/lottery/services/publicNotice.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@ const getRecentPurchaceItemListHandler = async (req, res) => {
}
};

const calculateProbabilityV2 = (users, weightSum, base, weight) => {
// 유저 수가 상품 수보다 적거나 같으면 무조건 상품을 받게된다.
if (users.length <= 15) return 1;

/**
* 경험적으로 발견한 사실
*
* p를 에어팟 당첨 확률, M을 전체 티켓 수라고 하자.
* 모든 유저의 p값이 1/15 미만일 경우, 실제 당첨 확률은 15p이다.
* 그렇지 않은 경우, 실제 당첨 확률은 1-a^Mp꼴의 지수함수를 따른다. (Note: Mp는 티켓 수이다.)
*
* 계산 과정
*
* a는 유저 수, 전체 티켓 수, 티켓 분포에 의해 결정되는 값으로, 현실적으로 계산하기 어렵다.
* 따라서, 모든 유저가 같은 수의 티켓을 가지고 있다고 가정하고 a를 계산한 뒤, 이를 확률 계산에 사용한다.
*
* a값의 계산 과정
*
* N을 유저 수라고 하자. 모든 유저가 같은 수의 티켓 M/N개를 가지고 있다고 하자.
* 이때 기대되는 당첨 확률은 직관적으로 15/N임을 알 수 있다. 즉, 1-a^(M/N) = 15/N이다.
* a에 대해 정리하면, a = (1-15/N)^(N/M)임을 알 수 있다.
*/
if (base !== null) return 1 - Math.pow(base, weight);
else return (weight / weightSum) * 15;
};

const getTicketLeaderboardHandler = async (req, res) => {
try {
const users = await eventStatusModel
Expand All @@ -84,12 +110,30 @@ const getTicketLeaderboardHandler = async (req, res) => {
const userId = isLogin(req) ? getLoginInfo(req).oid : null;
let rank = -1;

const weightSum = sortedUsers.reduce((before, user, index) => {
if (rank < 0 && user.userId === userId) {
rank = index;
}
return before + user.weight;
}, 0);
const [weightSum, totalTicket1Amount, totalTicket2Amount] =
sortedUsers.reduce(
(
[_weightSum, _totalTicket1Amount, _totalTicket2Amount],
user,
index
) => {
if (rank < 0 && user.userId === userId) {
rank = index;
}
return [
_weightSum + user.weight,
_totalTicket1Amount + user.ticket1Amount,
_totalTicket2Amount + user.ticket2Amount,
];
},
[0, 0, 0]
);

const isExponential =
sortedUsers.find((user) => user.weight >= weightSum / 15) !== undefined;
const base = isExponential
? Math.pow(1 - 15 / users.length, users.length / weightSum)
: null;

const leaderboard = await Promise.all(
sortedUsers.slice(0, 20).map(async (user) => {
Expand All @@ -104,6 +148,12 @@ const getTicketLeaderboardHandler = async (req, res) => {
ticket1Amount: user.ticket1Amount,
ticket2Amount: user.ticket2Amount,
probability: user.weight / weightSum,
probabilityV2: calculateProbabilityV2(
users,
weightSum,
base,
user.weight
),
};
})
);
Expand All @@ -112,13 +162,24 @@ const getTicketLeaderboardHandler = async (req, res) => {
.status(500)
.json({ error: "PublicNotice/Leaderboard : internal server error" });

if (rank >= 0)
res.json({
leaderboard,
rank: rank + 1,
probability: sortedUsers[rank].weight / weightSum,
});
else res.json({ leaderboard });
res.json({
leaderboard,
totalTicket1Amount,
totalTicket2Amount,
totalUserAmount: users.length,
...(rank >= 0
? {
rank: rank + 1,
probability: sortedUsers[rank].weight / weightSum,
probabilityV2: calculateProbabilityV2(
users,
weightSum,
base,
sortedUsers[rank].weight
),
}
: {}),
});
} catch (err) {
logger.error(err);
res
Expand Down

0 comments on commit 5017281

Please sign in to comment.