add punishment for dominating country

This commit is contained in:
HF 2023-12-27 15:43:34 +01:00
parent ccdf670291
commit 057f60f049
4 changed files with 144 additions and 20 deletions

View File

@ -11,6 +11,8 @@ import {
storeOnlinUserAmount,
getCountryDailyHistory,
getCountryRanks,
getHourlyCountryStats,
storeHourlyCountryStats,
getTopDailyHistory,
storeHourlyPixelsPlaced,
getHourlyPixelStats,
@ -20,30 +22,38 @@ import socketEvents from '../socket/socketEvents';
import logger from './logger';
import { MINUTE } from './constants';
import { PUNISH_DOMINATOR } from './config';
import { DailyCron, HourlyCron } from '../utils/cron';
class Ranks {
ranks = {
// ranking today of users by pixels
dailyRanking: [],
// ranking of users by pixels
ranking: [],
// ranking today of countries by pixels
dailyCRanking: [],
// ranking hourly of countries by pixels
cHourlyStats: [],
// yesterdays ranking of users by pixels
prevTop: [],
// online user amount by hour
onlineStats: [],
// ranking of countries by day
cHistStats: [],
// ranking of users by day
histStats: { users: [], stats: [] },
// pixels placed by hour
pHourlyStats: [],
// pixels placed by day
pDailyStats: [],
};
// if a country dominates, adjust its cooldown
#punishedCountry;
#punishmentFactor;
constructor() {
this.ranks = {
// ranking today of users by pixels
dailyRanking: [],
// ranking of users by pixels
ranking: [],
// ranking today of countries by pixels
dailyCRanking: [],
// yesterdays ranking of users by pixels
prevTop: [],
// online user amount by hour
onlineStats: [],
// ranking of countries by day
cHistStats: [],
// ranking of users by day
histStats: { users: [], stats: [] },
// pixels placed by hour
pHourlyStats: [],
// pixels placed by day
pDailyStats: [],
};
/*
* we go through socketEvents for sharding
*/
@ -70,12 +80,59 @@ class Ranks {
if (!newRanks) {
return;
}
if (newRanks.cHourlyStats) {
this.setPunishments(newRanks.cHourlyStats);
}
this.ranks = {
...this.ranks,
...newRanks,
};
}
setPunishments(cHourlyStats) {
if (!cHourlyStats.length || !PUNISH_DOMINATOR) {
return;
}
let outnumbered = 0;
const { cc: leadingCountry } = cHourlyStats[0];
let { px: margin } = cHourlyStats[0];
for (let i = 1; i < cHourlyStats.length; i += 1) {
margin -= cHourlyStats[i].px;
if (margin < 0) {
break;
}
outnumbered += 1;
}
/*
* if the leading country places more pixels
* than the fellowing 2+ countries after,
* 20% gets added to the cooldown for every following
* country, cailed at 200%;
*/
if (outnumbered > 2) {
this.#punishedCountry = leadingCountry;
let punishmentFactor = 1 + 0.2 * (outnumbered - 1);
if (punishmentFactor > 2) {
punishmentFactor = 2;
}
this.#punishmentFactor = punishmentFactor;
logger.info(
// eslint-disable-next-line max-len
`Punishment for dominating country ${leadingCountry} of ${punishmentFactor}`,
);
return;
}
this.#punishedCountry = null;
this.#punishmentFactor = 1.0;
}
getCountryCoolDownFactor(country) {
if (this.#punishedCountry === country) {
return this.#punishmentFactor;
}
return 1.0;
}
static async updateRanking() {
// only main shard does it
if (!socketEvents.amIImportant()) {
@ -107,10 +164,12 @@ class Ranks {
const onlineStats = await getOnlineUserStats();
const cHistStats = await getCountryDailyHistory();
const pHourlyStats = await getHourlyPixelStats();
const cHourlyStats = await getHourlyCountryStats(1, 100);
const ret = {
onlineStats,
cHistStats,
pHourlyStats,
cHourlyStats,
};
if (socketEvents.amIImportant()) {
// only main shard sends to others
@ -148,6 +207,7 @@ class Ranks {
const amount = socketEvents.onlineCounter.total;
await storeOnlinUserAmount(amount);
await storeHourlyPixelsPlaced();
await storeHourlyCountryStats(1, 100);
await Ranks.hourlyUpdateRanking();
}

View File

@ -29,6 +29,9 @@ export const BACKUP_DIR = process.env.BACKUP_DIR || null;
export const OUTGOING_ADDRESS = process.env.OUTGOING_ADDRESS || null;
// Punish when a country dominates
export const PUNISH_DOMINATOR = !!process.env.PUNISH_DOMINATOR;
// Proxycheck
export const USE_PROXYCHECK = parseInt(process.env.USE_PROXYCHECK, 10) || false;
export const { PROXYCHECK_KEY } = process.env;

View File

@ -8,6 +8,7 @@ import {
import logger, { pixelLogger } from './logger';
import allowPlace from '../data/redis/cooldown';
import socketEvents from '../socket/socketEvents';
import rankings from './Ranks';
import { setPixelByOffset } from './setPixel';
import isIPAllowed from './isAllowed';
import canvases from './canvases';
@ -127,6 +128,8 @@ export default async function drawByOffsets(
}
*/
factor *= rankings.getCountryCoolDownFactor(user.country);
factor *= 0.75;
const bcd = canvas.bcd * factor;

View File

@ -7,6 +7,9 @@ import { getDateKeyOfTs } from '../../core/utils';
export const RANKED_KEY = 'rank';
export const DAILY_RANKED_KEY = 'rankd';
export const DAILY_CRANKED_KEY = 'crankd';
export const HOURLY_CRANKED_KEY = 'crankh';
export const PREV_DAILY_CRANKED_KEY = 'pcrankd';
export const PREV_DAILY_CRANKED_TS_KEY = 'pcrankdts';
export const PREV_DAY_TOP_KEY = 'prankd';
const DAY_STATS_RANKS_KEY = 'ds';
const CDAY_STATS_RANKS_KEY = 'cds';
@ -100,6 +103,61 @@ export async function getCountryRanks(start, amount) {
return ranks;
}
/*
* get previous daily country ranking (one hour before cranks)
*/
export async function getHourlyCountryStats(start, amount) {
start -= 1;
amount -= 1;
let ranks = await client.zRangeWithScores(
HOURLY_CRANKED_KEY, start, start + amount, {
REV: true,
});
ranks = ranks.map((r) => ({
cc: r.value,
px: Number(r.score),
}));
return ranks;
}
export async function storeHourlyCountryStats(start, amount) {
start -= 1;
amount -= 1;
const tsNow = Date.now();
const prevTs = Number(await client.get(PREV_DAILY_CRANKED_TS_KEY));
const prevData = await client.zRangeWithScores(
PREV_DAILY_CRANKED_KEY, start, start + amount, {
REV: true,
});
await client.copy(DAILY_CRANKED_KEY, PREV_DAILY_CRANKED_KEY, {
REAPLACE: true,
});
await client.set(PREV_DAILY_CRANKED_TS_KEY, String(tsNow));
await client.del(HOURLY_CRANKED_KEY);
if (prevTs && prevTs > tsNow - 1000 * 3600 * 1.5) {
const curData = await client.zRangeWithScores(
DAILY_CRANKED_KEY, start, start + amount, {
REV: true,
});
const prevRanks = new Map();
prevData.forEach(({ value, score }) => prevRanks.set(value, score));
const addArr = [];
curData.forEach(({ value: cc, score: curPx }) => {
const prevPx = prevData.get(cc) || 0;
const px = (curPx > prevPx) ? curPx - prevPx : curPx;
addArr.push({
score: cc,
value: px,
});
});
if (addArr.length) {
await client.zAdd(HOURLY_CRANKED_KEY, addArr);
}
}
}
/*
* get top 10 from previous day
*/