import Database from 'better-sqlite3'; import ProgressBar from 'progress'; import db from '../database.js'; import {save_game_and_update_rating} from '../elo.js'; db.pragma('synchronous = OFF'); db.pragma('count_changes = OFF'); db.pragma('journal_mode = MEMORY'); db.pragma('temp_store = MEMORY'); const RANK_DIVISIONS = [ 'Cardboard', 'Wood', 'Wood+', 'Bronze', 'Bronze+', 'Silver', 'Silver+', 'Gold', 'Gold+', 'Platinum', 'Platinum+', 'Diamond', 'Diamond+', 'Legendary', ]; function get_rank_text(rank_float) { if (rank_float == 1.0) { return 'The One'; } // Epic rank distribution algorithm for (let i = 0; i < RANK_DIVISIONS.length; i++) { // Turn current 'Cardboard' rank into a value between 0 and 1 const rank_nb = (i + 1) / RANK_DIVISIONS.length; // To make climbing ranks more satisfying, we make lower ranks more common. // Visual representation: https://graphtoy.com/?f1(x,t)=1-((cos(x%5E0.8*%F0%9D%9C%8B)/2)+0.5)&v1=true&f2(x,t)=&v2=true&f3(x,t)=&v3=false&f4(x,t)=&v4=false&f5(x,t)=&v5=false&f6(x,t)=&v6=false&grid=true&coords=0.3918011117299855,0.3722110561434862,1.0068654346588846 const cutoff = 1 - ((Math.cos(Math.pow(rank_nb, 0.8) * Math.PI) / 2) + 0.5); if (rank_float < cutoff) { return RANK_DIVISIONS[i]; } } // Ok, floating point errors, who cares return RANK_DIVISIONS[RANK_DIVISIONS.length - 1]; } function save_s2_divisions() { db.exec('BEGIN TRANSACTION;'); console.info('Saving S2 divisions...'); const save_division = db.prepare(`UPDATE rating SET s2_division = ?, s2_scores = ? WHERE user_id = ? AND mode = ?`); const save_scores = db.prepare(`UPDATE rating SET s2_scores = ? WHERE user_id = ? AND mode = ?`); const old_db = new Database('old.db'); for (let mode = 0; mode < 4; mode++) { const ratings = old_db.prepare(`SELECT user_id, s1_scores, total_scores FROM rating WHERE mode = ? AND sig < 100 ORDER BY elo DESC`).all(mode); const bar = new ProgressBar('[:bar] :rate/s | :etas remaining', { complete: '=', incomplete: ' ', width: 20, total: ratings.length, }); for (let i = 0; i < ratings.length; i++) { const s2_scores = ratings[i].total_scores - ratings[i].s1_scores; if (s2_scores > 9) { const division = get_rank_text(1.0 - (i / ratings.length)); save_division.run(division, s2_scores, ratings[i].user_id, mode); } else { save_scores.run(s2_scores, ratings[i].user_id, mode); } bar.tick(1); } } db.exec('COMMIT;'); } async function glicko_to_elo() { db.exec('BEGIN TRANSACTION;'); console.info('Loading games...'); const old_db = new Database('old.db'); const games = old_db.prepare(`SELECT * FROM game ORDER BY game_id ASC`).all(); const get_scores = old_db.prepare(`SELECT * FROM score WHERE game_id = ?`); const bar = new ProgressBar('[:bar] :rate/s | :etas remaining', { complete: '=', incomplete: ' ', width: 20, total: games.length, }); for (const game of games) { const scores = get_scores.all(game.game_id); await save_game_and_update_rating({id: game.match_id}, { id: game.game_id, mode_int: game.play_mode, beatmap: { id: game.beatmap_id, }, start_time: new Date(game.start_time).toISOString(), end_time: new Date(game.end_time).toISOString(), scoring_type: game.scoring_type, team_type: game.team_type, mods: JSON.parse(game.mods), scores: scores.map((score) => { return { accuracy: score.accuracy, max_combo: score.max_combo, mods: JSON.parse(score.enabled_mods), statistics: { count_50: score.count_50, count_100: score.count_100, count_300: score.count_300, count_miss: score.count_miss, count_geki: score.count_geki, count_katu: score.count_katu, }, perfect: score.perfect, created_at: new Date(score.created_at).toISOString(), score: score.score, user_id: score.user_id, dodged: score.dodged, }; }), }); bar.tick(1); } console.info('Saving...'); db.exec('COMMIT;'); console.info('Done!'); } save_s2_divisions(); glicko_to_elo().catch(console.error);