From 4d7bdc955a2e6dd2c222f985c7fbc9b4febbccc4 Mon Sep 17 00:00:00 2001
From: Tor Andersson <tor@ccxvii.net>
Date: Sun, 13 Oct 2024 12:48:48 +0200
Subject: Tournaments!

---
 schema.sql | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 228 insertions(+), 21 deletions(-)

(limited to 'schema.sql')

diff --git a/schema.sql b/schema.sql
index 751d423..ee52bee 100644
--- a/schema.sql
+++ b/schema.sql
@@ -440,12 +440,15 @@ create view game_view as
 	select
 		games.*,
 		titles.title_name,
-		owner.name as owner_name
+		owner.name as owner_name,
+		tm_pools.pool_name
 	from
 		games
 		join titles using(title_id)
 		left join users as owner
 			on owner.user_id = games.owner_id
+		left join tm_rounds using(game_id)
+		left join tm_pools using(pool_id)
 	;
 
 drop view if exists game_view_public;
@@ -497,6 +500,7 @@ drop view if exists time_control_view;
 create view time_control_view as
 	select
 		game_id,
+		user_id,
 		role,
 		is_opposed
 	from
@@ -558,6 +562,228 @@ create view game_export_view as
 	from games as outer
 	;
 
+-- Tournaments --
+
+create table if not exists tm_seeds (
+	seed_id integer primary key,
+	seed_name text unique,
+
+	title_id text,
+	scenario text,
+	options text,
+	player_count integer,
+
+	pace integer default 2,
+
+	pool_size integer default 3,
+	round_count integer default 4,
+	is_concurrent boolean default 1,
+
+	level_count integer default 1,
+
+	is_open boolean default 1
+);
+
+create table if not exists tm_banned (
+	user_id integer primary key,
+	time datetime default current_timestamp
+);
+
+create table if not exists tm_queue (
+	user_id integer,
+	seed_id integer,
+	level integer,
+	is_paused boolean default 0,
+	time datetime default current_timestamp,
+	primary key (user_id, seed_id, level)
+);
+
+create table if not exists tm_pools (
+	pool_id integer primary key,
+	seed_id integer,
+	level integer,
+	is_finished boolean,
+	start_date datetime,
+	finish_date datetime,
+	pool_name text unique
+);
+
+create table if not exists tm_rounds (
+	game_id integer primary key,
+	pool_id integer,
+	round integer
+);
+
+create index if not exists tm_rounds_pool_idx on tm_rounds(pool_id);
+
+create table if not exists tm_results (
+	pool_id integer,
+	user_id integer,
+	points integer,
+	son integer,
+	primary key (pool_id, user_id)
+);
+
+create table if not exists tm_winners (
+	pool_id integer,
+	user_id integer
+);
+
+create index if not exists tm_winners_pool_idx on tm_winners(pool_id);
+
+drop view if exists tm_pool_active_view;
+create view tm_pool_active_view as
+	select
+		tm_pools.*,
+		sum(status > 1) || ' / ' || count(1) as status
+	from
+		tm_pools
+		left join tm_rounds using(pool_id)
+		left join games using(game_id)
+	where
+		not is_finished
+	group by
+		pool_id
+	order by
+		pool_name
+;
+
+drop view if exists tm_pool_finished_view;
+create view tm_pool_finished_view as
+	select
+		tm_pools.*,
+		group_concat(name) as status
+	from
+		tm_pools
+		left join tm_winners using(pool_id)
+		left join users using(user_id)
+	where
+		is_finished
+	group by
+		pool_id
+	order by
+		pool_name
+;
+
+drop view if exists tm_pool_view;
+create view tm_pool_view as
+	select * from tm_pool_active_view
+	union all
+	select * from tm_pool_finished_view
+;
+
+drop trigger if exists tm_trigger_update_results;
+create trigger tm_trigger_update_results after update of result on games when new.is_match
+begin
+	-- each player scores
+	update players
+		set score = (
+			case
+				when new.result is null then null
+				when new.result = role then 2
+				when new.result = 'Draw' then 1
+				when instr(new.result, role) then 1
+				else 0
+			end
+		)
+	where
+		players.game_id = new.game_id
+	;
+
+	-- Neustadtl Sonneborn–Berger tie-breaker
+	insert or replace into
+		tm_results (pool_id, user_id, points, son)
+	with
+		pts_cte as (
+			select
+				pool_id,
+				user_id,
+				sum(coalesce(score, 0)) as points
+			from
+				tm_rounds
+				join games using(game_id)
+				join players using(game_id)
+			where
+				pool_id = ( select pool_id from tm_rounds where game_id = new.game_id )
+			group by
+				user_id
+		),
+		son_cte as (
+			select
+				rr.pool_id,
+				p1.user_id,
+				sum(
+					case
+					when p1.score > p2.score then
+						pp.points * 2
+					when p1.score = p2.score then
+						pp.points
+					else
+						0
+					end
+				) as son
+			from
+				tm_rounds as rr
+				join games using(game_id)
+				join players as p1 using(game_id)
+				join players as p2 using(game_id)
+				join pts_cte pp on rr.pool_id = pp.pool_id and p2.user_id = pp.user_id
+			where
+				rr.pool_id = ( select pool_id from tm_rounds where game_id = new.game_id )
+				and p1.user_id != p2.user_id
+			group by
+				p1.user_id
+		)
+	select
+		pool_id, user_id, points, son
+	from
+		pts_cte
+		join son_cte using(pool_id, user_id)
+	;
+
+end;
+
+drop trigger if exists tm_trigger_update_winners;
+create trigger tm_trigger_update_winners after update of is_finished on tm_pools when new.is_finished
+begin
+	delete from tm_winners where pool_id = new.pool_id;
+	insert into
+		tm_winners ( pool_id, user_id )
+	with
+		tt as (
+			select
+				round_count as threshold
+			from
+				tm_seeds
+			where
+				seed_id = ( select seed_id from tm_pools where pool_id = new.pool_id )
+		),
+		aa as (
+			select
+				max(points) as max_points
+			from
+				tm_results
+			where
+				pool_id = new.pool_id
+		),
+		bb as (
+			select
+				max_points,
+				max(son) as max_son
+			from
+				tm_results, aa
+			where
+				pool_id = new.pool_id and points = max_points
+		)
+	select
+		pool_id, user_id
+	from
+		tm_results, bb, tt
+	where
+		pool_id = new.pool_id and points > threshold and points = max_points and son = max_son
+	;
+end;
+
 -- Trigger to update player counts when players join and part games
 
 drop trigger if exists trigger_join_game;
@@ -601,26 +827,6 @@ begin
 		games.game_id = old.game_id;
 end;
 
--- Trigger to update player score when game ends.
-
-drop trigger if exists trigger_update_score;
-create trigger trigger_update_score after update of result on games
-begin
-	update players
-		set score = (
-			case
-				when new.result is null then null
-				when new.result = role then 2
-				when new.result = 'Draw' then 1
-				when instr(new.result, role) then 1
-				else 0
-			end
-		)
-	where
-		players.game_id = new.game_id
-	;
-end;
-
 -- Trigger to track time spent!
 
 drop trigger if exists trigger_time_used_update;
@@ -673,6 +879,7 @@ begin
 	delete from posts where author_id = old.user_id;
 	delete from threads where author_id = old.user_id;
 	delete from game_chat where user_id = old.user_id;
+	delete from tm_queue where user_id = old.user_id;
 	delete from players where user_id = old.user_id and game_id in (select game_id from games where status = 0);
 	update games set owner_id = 0 where owner_id = old.user_id;
 end;
-- 
cgit v1.2.3

