Playing around with functions to generate brackets

main
noah metz 2024-01-31 22:20:09 -07:00
parent 02ad16a459
commit 2daec3e90b
1 changed files with 115 additions and 35 deletions

@ -3,13 +3,72 @@
-behaviour(gen_server).
-export([start_link/1, init/1, handle_cast/2, handle_call/3, terminate/2]).
-export([get_tick/0]).
-export([get_tick/0, generate_bracket/1, print_bracket/1]).
-record(state, {database_file = none, database = none, tick_start = none}).
-record(bracket, {depth = none, num = none, red = none, blue = none}).
first_error([]) -> ok;
first_error([ok | Rest]) -> first_error(Rest);
first_error([{rowid, _} | Rest]) -> first_error(Rest);
first_error([Error | _]) -> Error.
sql_begin(Database, Script) ->
ok = sqlite3:sql_exec(Database, "BEGIN;"),
Result = sqlite3:sql_exec_script(Database, Script),
case first_error(Result) of
ok -> sqlite3:sql_exec(Database, "COMMIT;");
_ ->
ok = sqlite3:sql_exec(Database, "ROLLBACK;"),
Result
end.
start_link(DatabaseFile) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [DatabaseFile], []).
higher_pow2(N) -> round(math:pow(2, ceil(math:log2(N)))).
first_layer(BracketSize, _, N) when N > BracketSize/2 -> [];
first_layer(BracketSize, Size, N) when BracketSize - N + 1 > Size ->
[N | first_layer(BracketSize, Size, N+1)];
first_layer(BracketSize, Size, N) ->
[{N, BracketSize - N + 1} | first_layer(BracketSize, Size, N+1)].
first_layer(Size) when Size > 1 ->
BracketSize = higher_pow2(Size),
first_layer(BracketSize, Size, 1).
merge_layer([Element], []) -> Element;
merge_layer([], Merged) ->
Final = lists:reverse(Merged),
merge_layer(Final);
merge_layer([First | Rest], Merged) ->
Last = lists:last(Rest),
List = lists:droplast(Rest),
merge_layer(List, [{First, Last} | Merged]).
merge_layer(List) -> merge_layer(List, []).
label_bracket({Left, Right}, [N | Counters], Depth) ->
{LC, L} = label_bracket(Left, Counters, Depth+1),
{RC, R} = label_bracket(Right, LC, Depth+1),
{[N+1 | RC], #bracket{depth = Depth, num=N, red=L, blue=R}};
label_bracket(Elem, Counters, _) -> {Counters, Elem}.
label_bracket(Bracket, Depth) ->
{_, Labeled} = label_bracket(Bracket, lists:duplicate(Depth, 1), 1),
Labeled.
generate_bracket(Size) ->
label_bracket(merge_layer(first_layer(Size)), ceil(math:log2(Size))).
print_bracket(Bracket) ->
RF = fun(R,L) when R == element(1,Bracket) ->
Flds = record_info(fields, bracket),
true = (L == length(Flds)),
Flds
end,
io:fwrite([io_lib_pretty:print(Bracket, RF),"\n"]).
open_db(DatabaseFile) ->
{ok, DB} = sqlite3:open(event_db, [{file, DatabaseFile}]),
ok = sqlite3:sql_exec(DB, "PRAGMA foreign_keys = ON;"),
@ -174,35 +233,27 @@ init_db(Database) ->
{foreign_key, {[team], teams, [team], ""}}]),
ok.
first_error([]) -> ok;
first_error([ok | Rest]) -> first_error(Rest);
first_error([{rowid, _} | Rest]) -> first_error(Rest);
first_error([Error | _]) -> Error.
delete_teams(Database, Teams) ->
SQL = lists:append(["BEGIN; ",
lists:append([io_lib:format("DELETE FROM teams WHERE team = ~p; ",
[Team]) || Team <- Teams]),
"COMMIT;"]),
first_error(sqlite3:sql_exec_script(Database, SQL)).
SQL = lists:append([io_lib:format("DELETE FROM teams WHERE team = ~p; ",
[Team])
|| Team <- Teams]),
sql_begin(Database, SQL).
add_teams(Database, Teams) ->
SQL = lists:append(["BEGIN; ",
lists:append([io_lib:format("INSERT INTO teams(team) VALUES('~s') ON CONFLICT(team) DO NOTHING; ",
[Team]) || Team <- Teams]),
"COMMIT;"]),
first_error(sqlite3:sql_exec_script(Database, SQL)).
SQL = lists:append([io_lib:format("INSERT INTO teams(team) VALUES('~s') ON CONFLICT(team) DO NOTHING; ",
[Team])
|| Team <- Teams]),
sql_begin(Database, SQL).
assign_divisions(Database, Teams) ->
SQL = lists:append(["BEGIN; ",
lists:append([io_lib:format("UPDATE teams SET division = '~s' WHERE team = '~s'; ", [Division, Team]) || {Team, Division} <- Teams]),
"COMMIT;"]),
first_error(sqlite3:sql_exec_script(Database, SQL)).
SQL = lists:append([io_lib:format("UPDATE teams SET division = '~s' WHERE team = '~s'; ",
[Division, Team])
|| {Team, Division} <- Teams]),
sql_begin(Database, SQL).
write_division_matches(Database, Division, Type, Matches) ->
Round = atom_to_list(Type),
SQL = lists:append(["BEGIN; ",
io_lib:format("DELETE FROM matches WHERE division = '~s' AND type = '~s';",
SQL = lists:append([io_lib:format("DELETE FROM matches WHERE division = '~s' AND type = '~s'; ",
[Division, Round]),
lists:append([io_lib:format(
"INSERT INTO matches(division, type, number) VALUES ('~s', '~s', ~p);
@ -211,9 +262,8 @@ write_division_matches(Database, Division, Type, Matches) ->
INSERT INTO match_teams(division, type, number, side, anum, team) VALUES ('~s', '~s', ~p, 'blue', 1, '~s');
INSERT INTO match_teams(division, type, number, side, anum, team) VALUES ('~s', '~s', ~p, 'blue', 2, '~s');",
[Division, Round, N, Division, Round, N, B1, Division, Round, N, B2, Division, Round, N, R1, Division, Round, N, R2])
|| {N, [B1, B2, R1, R2]} <- lists:enumerate(Matches)]),
"COMMIT;"]),
first_error(sqlite3:sql_exec_script(Database, SQL)).
|| {N, [B1, B2, R1, R2]} <- lists:enumerate(Matches)])]),
sql_begin(Database, SQL).
index_of(Item, List) -> index_of(Item, List, 1).
index_of(Item, [Element | _], N) when Item == Element -> N;
@ -278,8 +328,6 @@ handle_call({load_db, DatabaseFile}, _, State) ->
end, Teams))}
|| {Name} <- Divisions],
[_, {rows, Alliances}] = sqlite3:sql_exec(Database, "SELECT team, div"),
% Read matches and match_teams tables and publish to match/{div}/{round}/{number}
% Read match_sates and publish to match/{div}/{round}/{number}/state
% Read match_scores and publish to match/{div}/{round}/{number}/state
@ -352,16 +400,48 @@ handle_call({delete_division, Division}, _, State) ->
DELETE FROM divisions WHERE division = '~s';",
[Division, Division]))),
{reply, ok, State};
handle_call({generate_division, Division, Round, Size}, _, State) ->
handle_call({generate_practice, Division, Size}, _, State) ->
Teams = get_div_teams(State#state.database, Division),
Seed = rand:uniform(10000000),
io:fwrite("Generating division ~s/~s with teams ~p, size ~p, and seed ~p~n", [Division, Round, Teams, Size, Seed]),
case schedule:create(Seed, Size, Teams) of
error -> {reply, error, State};
Matches -> {reply, write_division_matches(State#state.database, Division, Round, Matches), State}
end;
Resp = case schedule:create(rand:uniform(10000000), Size, Teams) of
error -> error;
Matches -> write_division_matches(State#state.database, Division, practice, Matches)
end,
{reply, Resp, State};
handle_call({generate_qualification, Division, Size}, _, State) ->
Teams = get_div_teams(State#state.database, Division),
Resp = case schedule:create(rand:uniform(10000000), Size, Teams) of
error -> error;
Matches -> write_division_matches(State#state.database, Division, qualification, Matches)
end,
{reply, Resp, State};
handle_call({generate_elimination, Division, Size}, _, State) ->
BracketSize = higher_pow2(Size),
Total = Size-1,
SQL = lists:append(
[
io_lib:format("DELETE FROM alliances WHERE division = '~s'; ", [Division]),
io_lib:format("DELETE FROM matches WHERE division = '~s' AND type = 'elimination'; ", [Division]),
lists:append([io_lib:format("INSERT INTO alliances(division, number) VALUES('~s', ~p); ",
[Division, N])
|| N <- lists:seq(1, Total)]),
lists:append([io_lib:format("INSERT INTO matches(division, type, number)
VALUES('~s', 'elimination', ~p);
INSERT INTO elim_alliances(division, type, number, side, anum)
VALUES('~s', 'elimination', ~p, 'red', ~p);
INSERT INTO elim_alliances(division, type, number, side, anum)
VALUES('~s', 'elimination', ~p, 'blue', ~p);",
[Division, N, Division, N, BracketSize/2+N+1, Division, N, BracketSize/2-N])
|| N <- lists:seq(1, Size - BracketSize/2)]),
lists:append([io_lib:format("INSERT INTO elim_alliances(division, type, number, side, anum)
VALUES('~s', 'elimination', ~p, 'red', ~p);",
[Division, N, BracketSize/2-N+1])
|| N <- lists:seq(Size - BracketSize/2+1, BracketSize/2)])]),
{reply, sql_begin(State#state.database, SQL), State};
handle_call(generate_finals, _, State) ->
[{columns, _}, {rows, [{Size}]}] = sqlite3:sql_exec(State#state.database, "SELECT COUNT(*) FROM divisions;"),
{reply, noimpl, State};
handle_call(_, _, State) ->
{reply, nofunc, State}.
{reply, noimpl, State}.
get_div_teams(Database, Division) ->
[{columns, Columns}, {rows, Rows}] = sqlite3:sql_exec(Database, io_lib:format("SELECT team FROM teams WHERE division = ~p;", [Division])),