import sqlite3
import pandas as pd
import plotly.io as pio
from plotly import express as px
= "plotly_mimetype+notebook_connected" pio.renderers.default
In [93]:
In [94]:
= "thaumaturge_dpr.db"
db_file
def query(query: str) -> pd.DataFrame:
= sqlite3.connect(db_file)
con = pd.read_sql_query(query, con)
df
con.close()return df
In [95]:
= """
seed_query DROP TABLE IF EXISTS player;
CREATE TABLE player (
level INTEGER,
charisma INTEGER,
strength INTEGER,
esoteric_lore_training INTEGER,
weapon_proficiency INTEGER,
weapon_specialization INTEGER,
weapon_tracking INTEGER,
weapon_damage_dice INTEGER
);
INSERT INTO player (
level,
charisma,
strength,
esoteric_lore_training,
weapon_proficiency,
weapon_specialization,
weapon_tracking,
weapon_damage_dice
) VALUES
(1,4,3,2,2,0,0,1),
(2,4,3,2,2,0,1,1),
(3,4,3,4,2,0,1,1),
(4,4,3,4,2,0,1,2),
(5,4,4,4,4,2,1,2),
(6,4,4,4,4,2,1,2),
(7,4,4,6,4,2,1,2),
(8,4,4,6,4,2,1,2),
(9,4,4,6,4,2,1,2),
(10,5,4,6,4,2,2,2),
(11,5,4,6,4,2,2,2),
(12,5,4,6,4,2,2,3),
(13,5,4,6,6,3,2,3),
(14,5,4,6,6,3,2,3),
(15,5,5,8,6,3,2,3),
(16,5,5,8,6,3,3,3),
(17,5,5,8,6,3,3,3),
(18,5,5,8,6,3,3,3),
(19,5,5,8,6,3,3,4),
(20,6,5,8,6,3,3,4);
DROP TABLE IF EXISTS monster;
CREATE TABLE monster (
level INTEGER,
extreme_ac INTEGER,
hard_ac INTEGER,
moderate_ac INTEGER,
low_ac INTEGER,
dc INTEGER
);
delete from monster
;
INSERT INTO monster (level, extreme_ac, hard_ac, moderate_ac, low_ac, dc)
VALUES
(0,19,16,15,13,14),
(1,19,16,15,13,15),
(2,21,18,17,15,16),
(3,22,19,18,16,18),
(4,24,21,20,18,19),
(5,25,22,21,19,20),
(6,27,24,23,21,22),
(7,28,25,24,22,23),
(8,30,27,26,24,24),
(9,31,28,27,25,26),
(10,33,30,29,27,27),
(11,34,31,30,28,28),
(12,36,33,32,30,30),
(13,37,34,33,31,31),
(14,39,36,35,33,32),
(15,40,37,36,34,34),
(16,42,39,38,36,35),
(17,43,40,39,37,36),
(18,45,42,41,39,38),
(19,46,43,42,40,39),
(20,48,45,44,42,40),
(21,49,46,45,43,42),
(22,51,48,47,45,44),
(23,52,49,48,46,46),
(24,54,51,50,48,48);
DROP TABLE IF EXISTS level_diff;
CREATE TABLE level_diff (
value INTEGER
);
INSERT INTO level_diff
(value)
VALUES
(-4),
(-3),
(-2),
(-1),
(0),
(1),
(2),
(3),
(4);
DROP TABLE IF EXISTS weapon_die;
CREATE TABLE weapon_die (
die TEXT,
value DECIMAL(1,2)
);
INSERT INTO weapon_die
(die, value)
VALUES
('d4', '2.5'),
('d6', '3.5'),
('d8', '4.5'),
('d10', '5.5')
"""
= sqlite3.connect(db_file)
con with con:
for q in seed_query.split(";"):
+ ";")
con.execute(q con.close()
In [96]:
= "select level, dc from monster order by level"
q = query(q)
df = px.line(
fig
df,="level",
x="dc",
y={"level": "Monster Level", "dc": "Monster DC"},
labels="Monster DC by Level",
title=True,
markers
) fig.show()
In [97]:
= "select level, extreme_ac, hard_ac, moderate_ac, low_ac from monster order by level"
q = query(q)
df = df.melt(id_vars=["level"], var_name="tier", value_name="ac")
df = px.line(
fig
df,="level",
x="ac",
y="tier",
color={"level": "Monster Level", "ac": "Monster AC", "tier": "Tier"},
labels="Monster AC by Level",
title=True,
markers
) fig.show()
In [98]:
= """
level_diff_cte level_diff as """
In [99]:
= """
q select
level_diff.value as level_diff,
player.level as level,
monster.dc - player.level - player.charisma - player.esoteric_lore_training as success,
monster.dc + 10 - player.level - player.charisma - player.esoteric_lore_training as critical
from level_diff
cross join player
join monster on player.level - level_diff.value = monster.level
"""
= query(q)
df = px.line(
fig
df,="level",
x="success",
y="level_diff",
color={
labels"level": "Player level",
"success": "d20 success minimum",
"level_diff": "Player - monster level",
},="Exploit vulnerability success rate by player and monster level",
title=True,
markers
) fig.show()
In [100]:
= """
q with
player_attack as (
select
level,
player.level
+ player.strength
+ player.weapon_proficiency
+ player.weapon_tracking as attack_bonus
from player
)
select
level_diff.value as level_diff,
player.level as level,
monster.extreme_ac - player.attack_bonus as extreme,
monster.hard_ac - player.attack_bonus as hard,
monster.moderate_ac - player.attack_bonus as moderate,
monster.low_ac - player.attack_bonus as low
from level_diff
cross join player_attack as player
join monster on player.level + level_diff.value = monster.level
"""
= query(q)
df = df.melt(
df =["level", "level_diff"], var_name="tier", value_name="min_success_roll"
id_vars
)= px.line(
fig
df,="level",
x="min_success_roll",
y="level_diff",
color={
labels"level": "Player Level",
"min_success_roll": "d20 success minimum",
"level_diff": "Player - monster level",
},="tier",
facet_col=2,
facet_col_wrap=True,
markers
) fig.show()
In [101]:
= """
q select
weapon_die.die as die,
player.level as level,
weapon_die.value * player.weapon_damage_dice
+ player.strength
+ 2
+ player.weapon_specialization
+ 2
+ floor(player.level / 2) as damage
from player
cross join weapon_die
"""
= query(q)
df = px.line(
fig
df,="level",
x="damage",
y="die",
color={
labels"level": "Player Level",
"damage": "Damage per strike",
"die": "Weapon die size",
},=True,
markers
) fig.show()