Update Version (Beta): One word each new UTC hour
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
create extension if not exists pgcrypto;
|
||||
|
||||
create table if not exists public.wordle_words (
|
||||
position integer primary key,
|
||||
word text not null unique check (word ~ '^[a-z]{5}$')
|
||||
);
|
||||
|
||||
insert into public.wordle_words (position, word) values
|
||||
(1, 'cigar'),
|
||||
(2, 'rebut'),
|
||||
(3, 'sissy'),
|
||||
(4, 'humph'),
|
||||
(5, 'awake'),
|
||||
(6, 'blush'),
|
||||
(7, 'focal'),
|
||||
(8, 'evade'),
|
||||
(9, 'naval'),
|
||||
(10, 'serve'),
|
||||
(11, 'heath'),
|
||||
(12, 'dwarf'),
|
||||
(13, 'model'),
|
||||
(14, 'karma'),
|
||||
(15, 'stink'),
|
||||
(16, 'grade'),
|
||||
(17, 'quiet'),
|
||||
(18, 'bench'),
|
||||
(19, 'abate'),
|
||||
(20, 'feign'),
|
||||
(21, 'major'),
|
||||
(22, 'death'),
|
||||
(23, 'fresh'),
|
||||
(24, 'crust'),
|
||||
(25, 'stool'),
|
||||
(26, 'colon'),
|
||||
(27, 'abase'),
|
||||
(28, 'marry'),
|
||||
(29, 'react'),
|
||||
(30, 'batty'),
|
||||
(31, 'pride'),
|
||||
(32, 'floss'),
|
||||
(33, 'helix'),
|
||||
(34, 'croak'),
|
||||
(35, 'staff'),
|
||||
(36, 'paper'),
|
||||
(37, 'unfed'),
|
||||
(38, 'whelp'),
|
||||
(39, 'trawl'),
|
||||
(40, 'outdo'),
|
||||
(41, 'adobe'),
|
||||
(42, 'crazy'),
|
||||
(43, 'sower'),
|
||||
(44, 'repay'),
|
||||
(45, 'digit'),
|
||||
(46, 'crate'),
|
||||
(47, 'cluck'),
|
||||
(48, 'spike'),
|
||||
(49, 'mimic'),
|
||||
(50, 'pound'),
|
||||
(51, 'maxim'),
|
||||
(52, 'linen'),
|
||||
(53, 'unmet'),
|
||||
(54, 'flesh'),
|
||||
(55, 'booby'),
|
||||
(56, 'forth'),
|
||||
(57, 'first'),
|
||||
(58, 'stand'),
|
||||
(59, 'belly'),
|
||||
(60, 'ivory'),
|
||||
(61, 'seedy'),
|
||||
(62, 'print'),
|
||||
(63, 'yearn'),
|
||||
(64, 'drain'),
|
||||
(65, 'bribe'),
|
||||
(66, 'stout'),
|
||||
(67, 'panel'),
|
||||
(68, 'crass'),
|
||||
(69, 'flume'),
|
||||
(70, 'offal'),
|
||||
(71, 'agree'),
|
||||
(72, 'error'),
|
||||
(73, 'swirl'),
|
||||
(74, 'argue'),
|
||||
(75, 'bleed'),
|
||||
(76, 'delta'),
|
||||
(77, 'flick'),
|
||||
(78, 'totem'),
|
||||
(79, 'wooer'),
|
||||
(80, 'front'),
|
||||
(81, 'shrub'),
|
||||
(82, 'parry'),
|
||||
(83, 'biome'),
|
||||
(84, 'lapel'),
|
||||
(85, 'start'),
|
||||
(86, 'greet'),
|
||||
(87, 'goner'),
|
||||
(88, 'golem'),
|
||||
(89, 'lusty'),
|
||||
(90, 'loopy'),
|
||||
(91, 'round'),
|
||||
(92, 'audit'),
|
||||
(93, 'lying'),
|
||||
(94, 'gamma'),
|
||||
(95, 'labor'),
|
||||
(96, 'islet'),
|
||||
(97, 'civic'),
|
||||
(98, 'forge'),
|
||||
(99, 'corny'),
|
||||
(100, 'moult'),
|
||||
(101, 'basic'),
|
||||
(102, 'salad'),
|
||||
(103, 'agate'),
|
||||
(104, 'spicy'),
|
||||
(105, 'spray'),
|
||||
(106, 'essay'),
|
||||
(107, 'fjord'),
|
||||
(108, 'spend'),
|
||||
(109, 'kebab'),
|
||||
(110, 'guild'),
|
||||
(111, 'aback'),
|
||||
(112, 'motor'),
|
||||
(113, 'alone'),
|
||||
(114, 'hatch'),
|
||||
(115, 'hyper'),
|
||||
(116, 'thumb'),
|
||||
(117, 'dowry'),
|
||||
(118, 'ought'),
|
||||
(119, 'belch'),
|
||||
(120, 'dutch')
|
||||
on conflict (position) do nothing;
|
||||
|
||||
alter table public.wordle_words enable row level security;
|
||||
|
||||
create table if not exists public.wordle_rounds (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
user_id uuid not null references auth.users(id) on delete cascade,
|
||||
word text not null check (word ~ '^[a-z]{5}$'),
|
||||
hour_start timestamptz,
|
||||
started_at timestamptz not null default now(),
|
||||
next_playable_at timestamptz not null default (now() + interval '1 hour'),
|
||||
completed_at timestamptz,
|
||||
won boolean,
|
||||
guess_count integer check (guess_count between 1 and 6),
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
alter table public.wordle_rounds
|
||||
add column if not exists hour_start timestamptz;
|
||||
|
||||
update public.wordle_rounds
|
||||
set hour_start = date_trunc('hour', started_at)
|
||||
where hour_start is null;
|
||||
|
||||
alter table public.wordle_rounds
|
||||
alter column hour_start set not null;
|
||||
|
||||
create index if not exists wordle_rounds_user_started_idx
|
||||
on public.wordle_rounds (user_id, started_at desc);
|
||||
|
||||
create unique index if not exists wordle_rounds_user_hour_idx
|
||||
on public.wordle_rounds (user_id, hour_start);
|
||||
|
||||
alter table public.wordle_rounds enable row level security;
|
||||
|
||||
drop policy if exists "Users can read their own rounds" on public.wordle_rounds;
|
||||
create policy "Users can read their own rounds"
|
||||
on public.wordle_rounds
|
||||
for select
|
||||
using (auth.uid() = user_id);
|
||||
|
||||
drop function if exists public.start_hourly_round(text);
|
||||
|
||||
create or replace function public.start_hourly_round()
|
||||
returns table (
|
||||
round_id uuid,
|
||||
word text,
|
||||
hour_start timestamptz,
|
||||
started_at timestamptz,
|
||||
next_playable_at timestamptz,
|
||||
completed_at timestamptz,
|
||||
won boolean,
|
||||
guess_count integer,
|
||||
server_now timestamptz,
|
||||
is_existing boolean
|
||||
)
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
declare
|
||||
existing_round public.wordle_rounds%rowtype;
|
||||
new_round public.wordle_rounds%rowtype;
|
||||
current_hour timestamptz := date_trunc('hour', now());
|
||||
word_count integer;
|
||||
word_offset integer;
|
||||
hourly_word text;
|
||||
begin
|
||||
if auth.uid() is null then
|
||||
raise exception 'Authentication required';
|
||||
end if;
|
||||
|
||||
select count(*) into word_count from public.wordle_words;
|
||||
if word_count = 0 then
|
||||
raise exception 'No hourly words are configured';
|
||||
end if;
|
||||
|
||||
word_offset := (floor(extract(epoch from current_hour) / 3600)::bigint % word_count)::integer;
|
||||
|
||||
select public.wordle_words.word
|
||||
into hourly_word
|
||||
from public.wordle_words
|
||||
order by position
|
||||
limit 1 offset word_offset;
|
||||
|
||||
select *
|
||||
into existing_round
|
||||
from public.wordle_rounds
|
||||
where user_id = auth.uid()
|
||||
and public.wordle_rounds.hour_start = current_hour
|
||||
order by started_at desc
|
||||
limit 1;
|
||||
|
||||
if found then
|
||||
return query select
|
||||
existing_round.id,
|
||||
existing_round.word,
|
||||
existing_round.hour_start,
|
||||
existing_round.started_at,
|
||||
existing_round.next_playable_at,
|
||||
existing_round.completed_at,
|
||||
existing_round.won,
|
||||
existing_round.guess_count,
|
||||
now(),
|
||||
true;
|
||||
return;
|
||||
end if;
|
||||
|
||||
insert into public.wordle_rounds (user_id, word, hour_start, next_playable_at)
|
||||
values (auth.uid(), hourly_word, current_hour, current_hour + interval '1 hour')
|
||||
returning * into new_round;
|
||||
|
||||
return query select
|
||||
new_round.id,
|
||||
new_round.word,
|
||||
new_round.hour_start,
|
||||
new_round.started_at,
|
||||
new_round.next_playable_at,
|
||||
new_round.completed_at,
|
||||
new_round.won,
|
||||
new_round.guess_count,
|
||||
now(),
|
||||
false;
|
||||
end;
|
||||
$$;
|
||||
|
||||
create or replace function public.complete_hourly_round(
|
||||
round_id uuid,
|
||||
did_win boolean,
|
||||
guess_total integer
|
||||
)
|
||||
returns void
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
begin
|
||||
if auth.uid() is null then
|
||||
raise exception 'Authentication required';
|
||||
end if;
|
||||
|
||||
if guess_total < 1 or guess_total > 6 then
|
||||
raise exception 'Guess total must be between 1 and 6';
|
||||
end if;
|
||||
|
||||
update public.wordle_rounds
|
||||
set completed_at = coalesce(completed_at, now()),
|
||||
won = did_win,
|
||||
guess_count = guess_total
|
||||
where id = round_id
|
||||
and user_id = auth.uid();
|
||||
end;
|
||||
$$;
|
||||
|
||||
revoke all on public.wordle_rounds from anon, authenticated;
|
||||
revoke all on public.wordle_words from anon, authenticated;
|
||||
grant select on public.wordle_rounds to authenticated;
|
||||
grant execute on function public.start_hourly_round() to authenticated;
|
||||
grant execute on function public.complete_hourly_round(uuid, boolean, integer) to authenticated;
|
||||
Reference in New Issue
Block a user