A Nix-friendly SQLite-enhanced fork of Flitter, a speedrunning split timer for Unix-style terminals
リビジョン | afe9c57f707a0b7e271d3083466e8c77742bde9e (tree) |
---|---|
日時 | 2023-06-06 02:09:56 |
作者 | Corbin <cds@corb...> |
コミッター | Corbin |
Fix a SQL query and add cumulative times per attempt.
@@ -59,6 +59,32 @@ Keybindings are all global hotkeys; they will work even when the terminal is not | ||
59 | 59 | | `Delete` | Pause / delete run (don't save anything) | |
60 | 60 | | `Q` | Quit (if not currently timing) | |
61 | 61 | |
62 | +### Summaries | |
63 | + | |
64 | +The `flitter-summary` tool can be used to quickly extract the minimum (best), | |
65 | +average, and standard deviation for an entire database, a single category, or a | |
66 | +single run. For example: | |
67 | + | |
68 | +```bash | |
69 | +$ flitter-summary splits.db | |
70 | +... | |
71 | +Horizon: Forbidden West (Abs%): min 33:09.354 (attempt 4), avg 38:17.799 ± 4:02.849 | |
72 | +... | |
73 | + | |
74 | +$ flitter-summary splits.db 'Horizon: Forbidden West' 'Abs%' | |
75 | +Segment: New Game -> Interlude: min 15:23.983, avg 15:36.073 ± 11.095 | |
76 | +Segment: New Game -> Shuttle: min 13:53.595, avg 18:31.488 ± 6:05.001 | |
77 | +Segment: Shuttle -> Spire: min 2:13.510, avg 2:35.034 ± 17.954 | |
78 | +Segment: Spire -> Embassy: min 14:47.481, avg 18:00.206 ± 1:42.837 | |
79 | +Segment: Embassy -> Abs: min 2:14.768, avg 3:09.039 ± 1:06.530 | |
80 | + | |
81 | +$ flitter-summary splits.db 'Horizon: Forbidden West' 'Abs%' 4 | |
82 | +Segment: New Game -> Shuttle: 13:53.595 (cum 13:53.595) | |
83 | +Segment: Shuttle -> Spire: 2:13.510 (cum 16:07.105) | |
84 | +Segment: Spire -> Embassy: 14:47.481 (cum 30:54.586) | |
85 | +Segment: Embassy -> Abs: 2:14.768 (cum 33:09.354) | |
86 | +``` | |
87 | + | |
62 | 88 | ## Contributing |
63 | 89 | |
64 | 90 | Feel free to make an issue or a pull request! Also feel free to copy any |
@@ -51,22 +51,25 @@ let () = | ||
51 | 51 | ] |
52 | 52 | in |
53 | 53 | let stmt = |
54 | - (* XXX SELECT DISTINCT required because path sometimes repeats i *) | |
54 | + (* SELECT DISTINCT required because path sometimes repeats i *) | |
55 | 55 | Storage.prep db |
56 | 56 | "with path(start, end, i) as (select segments.starting_at, \ |
57 | - segments.ending_at, 0 from segments, routes where routes.game = \ | |
58 | - ?1 and routes.category = ?2 and segments.starting_at = \ | |
59 | - routes.starting_at union select starting_at, ending_at, path.i \ | |
60 | - + 1 from segments, path where game = ?1 and category = ?2 and \ | |
61 | - attempt = ?3 and starting_at = path.end) select distinct \ | |
62 | - starting_at, ending_at, duration from path, segments where game \ | |
63 | - = ?1 and category = ?2 and attempt = ?3 and starting_at = \ | |
64 | - path.start and ending_at = path.end order by path.i;" | |
57 | + segments.ending_at, 0 from segments, routes using (starting_at) \ | |
58 | + where routes.game=?1 and routes.category=?2 union select \ | |
59 | + starting_at, ending_at, path.i+1 from segments, path where \ | |
60 | + game=?1 and category=?2 and attempt=?3 and \ | |
61 | + starting_at=path.end), segs(starting_at, ending_at, duration) \ | |
62 | + as (select distinct starting_at, ending_at, duration from path, \ | |
63 | + segments where game=?1 and category=?2 and attempt=?3 and \ | |
64 | + starting_at=path.start and ending_at=path.end order by path.i) \ | |
65 | + select starting_at, ending_at, duration, sum(duration) over \ | |
66 | + (rows between unbounded preceding and current row) from segs;" | |
65 | 67 | in |
66 | 68 | Storage.check_select stmt vals (fun row -> |
67 | 69 | print_string |
68 | - (sprintf "Segment: %s -> %s: %s\n" | |
70 | + (sprintf "Segment: %s -> %s: %s (cum %s)\n" | |
69 | 71 | (Data.to_string_exn row.(0)) |
70 | 72 | (Data.to_string_exn row.(1)) |
71 | - (Duration.to_string (Data.to_int_exn row.(2)) 3)))) | |
73 | + (Duration.to_string (Data.to_int_exn row.(2)) 3) | |
74 | + (Duration.to_string (Data.to_int_exn row.(3)) 3)))) | |
72 | 75 | | _ -> print_string usage |
@@ -10,8 +10,8 @@ let sql = | ||
10 | 10 | segments.ending_at, 0 from segments, routes using (starting_at) where \ |
11 | 11 | routes.game=?1 and routes.category=?2 union select starting_at, ending_at, \ |
12 | 12 | path.i+1 from segments, path where game=?1 and category=?2 and \ |
13 | - starting_at=path.end) select starting_at, ending_at, min(duration), \ | |
14 | - avg(duration), stdev(duration) from path, segments where game=?1 and \ | |
13 | + starting_at=path.end) select starting_at, ending_at, avg(duration), \ | |
14 | + stdev(duration), min(duration) from path, segments where game=?1 and \ | |
15 | 15 | starting_at=path.start and ending_at=path.end group by starting_at, \ |
16 | 16 | ending_at order by path.i;" |
17 | 17 |
@@ -23,9 +23,9 @@ let from_db db game category = | ||
23 | 23 | ( Sqlite3.Data.to_string_exn row.(0), |
24 | 24 | Sqlite3.Data.to_string_exn row.(1), |
25 | 25 | Gold.of_stats |
26 | - ( Some (Sqlite3.Data.to_int_exn row.(2)), | |
26 | + ( Some (int_of_float (Sqlite3.Data.to_float_exn row.(2))), | |
27 | 27 | Some (int_of_float (Sqlite3.Data.to_float_exn row.(3))), |
28 | - Some (int_of_float (Sqlite3.Data.to_float_exn row.(4))) ) )) ) | |
28 | + Some (Sqlite3.Data.to_int_exn row.(4)))))) | |
29 | 29 | |
30 | 30 | let map f (i, l) = (i, List.map ~f:(fun (s, t, x) -> (s, t, f x)) l) |
31 | 31 | let iter f (_, l) = List.iter ~f:(fun (s, t, x) -> f x) l |