Compare commits

...

7 Commits

Author SHA1 Message Date
5f9ae3e145
track knockouts 2023-03-27 21:51:50 +11:00
83093f8c81
remove queries
outsourced
2023-03-27 21:51:31 +11:00
e1b5d3abb0
track switches by player 2023-03-27 20:19:58 +11:00
ef70128552
track knockouts 2023-03-27 20:17:46 +11:00
73554214f7
add metadata for external usage 2023-03-27 20:17:18 +11:00
97776d7c2d
add last game of week 3 2023-03-27 19:53:56 +11:00
e597fedc2f
add future notes 2023-03-26 20:08:45 +11:00
3 changed files with 48 additions and 138 deletions

View File

@ -18,3 +18,9 @@ usage: hhirlstats [-h] [-v] [-c] [-Q {gametime,moves,nicknames,playtime,usage}]
Recommended to cache replays (`-c`) to save issuing requests to Showdown on every query.
Run the program once with all your replays (or once for each replay), then start querying.
## Future work
- include timestamps in logs to correlate KOs with the mon that KOed
- calculate gametime based on active turns rather than moves used
- also solves the issue where paralyzed/confused turns are not counted

View File

@ -12,3 +12,4 @@ https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-29698
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-30488
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-30858
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-31241
https://replay.pokemonshowdown.com/dl-gen9paldeadexposthomedraft-31809

179
main.py
View File

@ -46,17 +46,21 @@ def _init_db(conn: sqlite3.Connection):
UNIQUE(game, turn, player, user)
);
CREATE TABLE IF NOT EXISTS switches(
game, turn, name,
UNIQUE(game, turn, name)
game, turn, player, name,
UNIQUE(game, turn, player, name)
);
CREATE TABLE IF NOT EXISTS nicknames(
game, player, name, specie,
UNIQUE(game,player, specie)
UNIQUE(game, player, specie)
);
"""
)
conn.execute(
"""
CREATE TABLE IF NOT EXISTS knockouts(
game, turn, player, name,
UNIQUE(game, turn, player)
);
CREATE TABLE IF NOT EXISTS games(
id, p1, p2, format, uploadtime,
UNIQUE(id)
)
"""
)
@ -96,11 +100,11 @@ def parse_log(game: str, log: str, into: sqlite3.Connection):
player, name = resolve_mon(name)
conn.execute(
"""
INSERT INTO switches(game, turn, name)
VALUES (?, ?, ?)
INSERT INTO switches(game, turn, player, name)
VALUES (?, ?, ?, ?)
ON CONFLICT DO NOTHING
""",
(game, turn, name),
(game, turn, player, name),
)
conn.execute(
"""
@ -110,116 +114,20 @@ def parse_log(game: str, log: str, into: sqlite3.Connection):
""",
(game, player, name, specie.split(", ")[0]),
)
case ["faint", mon]:
player, mon = resolve_mon(mon)
conn.execute(
"""
INSERT INTO knockouts(game, turn, player, name)
VALUES(?, ?, ?, ?)
ON CONFLICT DO NOTHING
""",
(game, turn, player, mon),
)
case _:
debug(f"unhandled message {chunks[0]}")
QUERIES = ["gametime", "moves", "nicknames", "playtime", "usage"]
def query(type: str, conn: sqlite3.Connection):
match type:
case "gametime":
print("Longest games")
print("=============")
for row in conn.execute(
"""
SELECT game, MAX(turn) AS n
FROM moves
GROUP BY game
ORDER BY n DESC
LIMIT 5
"""
):
replay = fetch(row.game, cache=True)
print(f"{replay.p1} vs {replay.p2}: {row.n} turns")
case "moves":
print("Move usage overall")
print("==================")
for row in conn.execute(
"""
SELECT name, COUNT(*) AS n
FROM moves
GROUP BY name
ORDER BY n DESC, name
LIMIT 10
"""
):
print(f"{row.name}: {row.n}")
case "nicknames":
print("Nickname usage per player")
print("=========================")
for row_p in conn.execute("SELECT DISTINCT player FROM nicknames"):
print(row_p.player)
for row_s in conn.execute(
"SELECT DISTINCT specie FROM nicknames WHERE player = ?",
(row_p.player,),
):
print(f" {row_s.specie}: ", end="")
names = []
for row in conn.execute(
"""
SELECT player, specie, name, count(game) AS n
FROM nicknames
WHERE player = ? AND specie = ?
GROUP BY player, specie, name
ORDER BY player, specie, name
""",
(row_p.player, row_s.specie),
):
names.append(f"{row.name} (x{row.n})")
print(*names, sep=", ")
case "playtime":
print("Active playtime per Pokemon")
print("===========================")
for row in conn.execute(
"""
SELECT m.player, k.specie, COUNT(m.name) AS n
FROM moves m
LEFT JOIN nicknames k ON (m.game, m.player, m.user) = (k.game, k.player, k.name)
GROUP BY k.specie, m.player
ORDER BY n DESC, k.specie, m.player
LIMIT 10
"""
):
print(f"{row.specie} ({row.player}): {row.n} turns")
case "usage":
print("Pokemon usage per player")
print("========================")
games = {
r.player: r.n
for r in conn.execute(
"""
SELECT player, COUNT(m.game) AS n
FROM (SELECT DISTINCT player, game FROM moves) m
GROUP BY player
"""
)
}
for row_p in conn.execute("SELECT DISTINCT player FROM nicknames"):
print(row_p.player)
for row_s in conn.execute(
"""
SELECT specie, COUNT(game) AS n
FROM nicknames
WHERE player = ?
GROUP BY specie
ORDER BY n DESC, specie
""",
(row_p.player,),
):
print(
f" {row_s.specie}: {row_s.n}"
f" ({row_s.n / games[row_p.player] * 100:.2f}%)"
)
case _:
error(f"unknown query {type}")
@dataclass(frozen=True)
class Replay:
id: str
@ -266,13 +174,7 @@ def main():
"-v", "--verbose", action="store_true", help="add debugging info"
)
parser.add_argument("-c", "--cache", action="store_true", help="cache replays")
parser.add_argument(
"-Q",
"--query",
choices=QUERIES,
help="run query instead of download",
)
parser.add_argument("replay", nargs="*", help="replay ID or URL")
parser.add_argument("replay", nargs="+", help="replay ID or URL")
args = parser.parse_args(sys.argv[1:])
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
@ -281,23 +183,24 @@ def main():
db = sqlite3.connect("data.db")
_init_db(db)
if args.query:
query(args.query, db)
else:
if not args.replay:
parser.print_usage()
print(f"{APP}: error: either query or replay arguments are required")
sys.exit(1)
for r in args.replay:
try:
replay = fetch(r, cache=args.cache)
except Exception as e:
error(f"bad replay {r}")
continue
for r in args.replay:
try:
replay = fetch(r, cache=args.cache)
except Exception as e:
error(f"bad replay {r}")
continue
db.execute(
"""
INSERT INTO games(id, p1, p2, format, uploadtime)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT DO NOTHING
""",
(replay.id, replay.p1, replay.p2, replay.format, replay.uploadtime),
)
parse_log(replay.id, replay.log, into=db)
db.commit()
parse_log(replay.id, replay.log, into=db)
db.commit()
finally:
db.close()