summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2024-01-02 16:28:48 +0100
committerTor Andersson <tor@ccxvii.net>2024-01-02 17:06:50 +0100
commitf794c5379baf12d6dbc4b4d02a1e44604e1def00 (patch)
tree4da8d1fe1badc05437ec8e862a32c77be64a47b6
parentc7fb66f0903d4d7b7e6c04dc33a3e5e9d3a3068d (diff)
downloadserver-f794c5379baf12d6dbc4b4d02a1e44604e1def00.tar.gz
Handle multiple winners for Elo calculations.
-rw-r--r--schema.sql2
-rw-r--r--server.js25
-rw-r--r--tools/elo.js38
3 files changed, 41 insertions, 24 deletions
diff --git a/schema.sql b/schema.sql
index 9143a9e..f04425f 100644
--- a/schema.sql
+++ b/schema.sql
@@ -169,7 +169,7 @@ create view rated_games_view as
games
where
status > 1
- and moves > 6
+ and moves >= player_count * 3
and user_count = player_count
and player_count > 1
and not exists (
diff --git a/server.js b/server.js
index 3b0792f..7006aab 100644
--- a/server.js
+++ b/server.js
@@ -2056,6 +2056,11 @@ const SQL_SELECT_RATING_GAME = SQL("select * from rated_games_view where game_id
const SQL_SELECT_RATING_PLAYERS = SQL("select * from player_rating_view where game_id=?")
const SQL_INSERT_RATING = SQL("insert or replace into ratings (title_id,user_id,rating,count,last) values (?,?,?,?,?)")
+function is_winner(role, result) {
+ // NOTE: uses substring matching for multiple winners instead of splitting result on comma.
+ return (result === "Draw" || result === role || result.includes(role))
+}
+
function elo_k(a) {
return a.count < 10 ? 60 : 30
}
@@ -2080,18 +2085,24 @@ function update_elo_ratings(game_id) {
if (!game)
return
+ if (!game.result || game.result === "None")
+ return
+
let players = SQL_SELECT_RATING_PLAYERS.all(game_id)
- let winner = null
+ let winners = 0
for (let p of players)
- if (p.role === game.result)
- winner = p
+ if (is_winner(p.role, game.result))
+ winners ++
+
+ if (winners === 0)
+ return
for (let p of players)
- if (winner !== null)
- p.change = elo_change(p, players, p === winner ? 1 : 0)
+ if (is_winner(p.role, game.result))
+ p.change = elo_change(p, players, 1 / winners)
else
- p.change = elo_change(p, players, 1 / players.length)
+ p.change = elo_change(p, players, 0)
for (let p of players)
SQL_INSERT_RATING.run(game.title_id, p.user_id, p.rating + p.change, p.count + 1, game.mtime)
@@ -2395,7 +2406,7 @@ const QUERY_PURGE_FINISHED_GAMES = SQL(`
games
where
status > 1
- and ( not is_opposed or moves <= player_count * 3 )
+ and ( not is_opposed or moves < player_count * 3 )
and julianday(mtime) < julianday('now', '-10 days')
`)
diff --git a/tools/elo.js b/tools/elo.js
index a6f9c9d..ad0dbad 100644
--- a/tools/elo.js
+++ b/tools/elo.js
@@ -8,6 +8,11 @@ const SQL_SELECT_GAMES = db.prepare("select * from rated_games_view order by mti
const SQL_SELECT_RATING = db.prepare("select * from player_rating_view where game_id=?")
const SQL_INSERT_RATING = db.prepare("insert or replace into ratings (title_id,user_id,rating,count,last) values (?,?,?,?,?)")
+function is_winner(role, result) {
+ // NOTE: uses substring matching for multiple winners instead of splitting result on comma.
+ return (result === "Draw" || result === role || result.includes(role))
+}
+
function elo_k(n) {
return n < 10 ? 60 : 30
}
@@ -27,28 +32,29 @@ function elo_change(a, players, s) {
function update_elo_ratings(game) {
let players = SQL_SELECT_RATING.all(game.game_id)
+
if (game.player_count !== players.length)
return
- let winner = null
+ if (!game.result || game.result === "None")
+ return
+
+ let winners = 0
+ for (let p of players)
+ if (is_winner(p.role, game.result))
+ winners ++
+
+ if (winners === 0)
+ return
+
for (let p of players)
- if (p.role === game.result)
- winner = p
-
- if (winner) {
- for (let p of players) {
- if (p === winner)
- p.change = elo_change(p, players, 1)
- else
- p.change = elo_change(p, players, 0)
- }
- } else {
- for (let p of players)
- p.change = elo_change(p, players, 1 / game.player_count)
- }
+ if (is_winner(p.role, game.result))
+ p.change = elo_change(p, players, 1 / winners)
+ else
+ p.change = elo_change(p, players, 0)
for (let p of players)
- SQL_INSERT_RATING.run(game.title_id, p.user_id, p.rating + p.change, p.count+1, game.xtime)
+ SQL_INSERT_RATING.run(game.title_id, p.user_id, p.rating + p.change, p.count + 1, game.mtime)
}
db.exec("begin transaction")