use db to calculate best quest for item

main
Bryce Allen 11 years ago
parent 45ad33d7cd
commit 835aec3406

1
.gitignore vendored

@ -0,0 +1 @@
*.pyc

@ -0,0 +1,122 @@
import sqlite3
class Quest(object):
def __init__(self, quest_row, quest_rewards):
self._row = quest_row
self._rewards = quest_rewards
self.id = quest_row["_id"]
self.name = quest_row["name"]
self.stars = quest_row["stars"]
self.hub = quest_row["hub"]
self.goal = quest_row["goal"]
self.rank = get_rank(self.hub, self.stars)
def __str__(self):
return "%s (%s %s* %s)" % (self.name, self.hub, self.stars, self.rank)
def get_rank(hub, stars):
if hub == "Caravan":
if stars < 6:
return "LR"
elif stars < 10:
return "HR"
return "G"
if hub == "Guild":
if stars < 4:
return "LR"
elif stars < 8:
return "HR"
return "G"
class MHDB(object):
def __init__(self, path):
self.conn = sqlite3.connect(path)
self.conn.row_factory = sqlite3.Row
self.cache = {}
def _get_memoized(self, key, query, *args):
if key in self.cache:
v = self.cache[key].get(args)
if v is not None:
return v
else:
self.cache[key] = {}
cursor = self.conn.execute(query, args)
v = self.cache[key][args] = cursor.fetchall()
return v
def get_item(self, item_id):
v = self._get_memoized("item", """
SELECT * FROM items
WHERE _id=?
""", item_id)
return v[0]
def get_item_by_name(self, name):
v = self._get_memoized("item", """
SELECT * FROM items
WHERE name=?
""", name)
return v[0]
def get_monster_by_name(self, name):
v = self._get_memoized("monster", """
SELECT * FROM monsters
WHERE name=?
""", name)
return v[0]
def get_monster(self, monster_id):
v = self._get_memoized("monster", """
SELECT * FROM monsters
WHERE _id=?
""", monster_id)
return v[0]
def get_quest(self, quest_id):
v = self._get_memoized("quest", """
SELECT * FROM quests
WHERE _id=?
""", quest_id)
return v[0]
def get_quest_rewards(self, quest_id):
v = self._get_memoized("quest_rewards", """
SELECT * FROM quest_rewards
WHERE quest_id=?
""", quest_id)
return v
def get_monster_rewards(self, monster_id, rank):
v = self._get_memoized("monster_rewards", """
SELECT * FROM hunting_rewards
WHERE monster_id=? AND rank=?
""", monster_id, rank)
return v
def get_quest_monsters(self, quest_id):
v = self._get_memoized("quest_monsters", """
SELECT monster_id FROM monster_to_quest
WHERE quest_id=? AND unstable='no'
""", quest_id)
return v
def get_item_quests(self, item_id):
cursor = self.conn.execute("""
SELECT DISTINCT quest_id FROM quest_rewards
WHERE item_id=?
""", (item_id,))
rows = cursor.fetchall()
quests = []
for r in rows:
quest_row = self.get_quest(r["quest_id"])
rewards_rows = self.get_quest_rewards(r["quest_id"])
quests.append(Quest(quest_row, rewards_rows))
return quests

@ -43,6 +43,27 @@
# B: ./mhprop.py 5 1 1 69
# For great luck, replace 69 with 90
CAP_SKILL_NONE = 0
CAP_SKILL_EXPERT = 1
CAP_SKILL_MASTER = 2
CAP_SKILL_GOD = 3
LUCK_SKILL_NONE = 0
LUCK_SKILL_GOOD = 1
LUCK_SKILL_GREAT = 2
QUEST_A = "A"
QUEST_B = "B"
QUEST_SUB = "Sub"
CARVING_SKILL_NONE = 0
CARVING_SKILL_PRO = 1
CARVING_SKILL_FELYNE_LOW = 2
CARVING_SKILL_FELYNE_HI = 3
CARVING_SKILL_CELEBRITY = 4
CARVING_SKILL_GOD = 5
import sys
def _quest_reward_p(reward_percent, reward_count):
@ -84,6 +105,105 @@ def quest_reward_p(reward_percent, min_rewards, max_rewards, extend_percent=69):
return p * 100
def reward_expected_c(min_rewards, max_rewards, extend_percent):
total_p = 0.0
expected_attempts = 0.0
for reward_count in xrange(min_rewards, max_rewards + 1):
p = _reward_count_p(reward_count, min_rewards, max_rewards,
extend_percent)
expected_attempts += p * reward_count
#print "P(C = %d) = %0.4f" % (reward_count, p)
total_p += p
assert abs(total_p - 1.0) < .00001, "total = %f" % total_p
return expected_attempts
def quest_reward_expected_c(line=QUEST_A, luck_skill=LUCK_SKILL_NONE):
"""
Expected number of rewards from specified quest line with given skills.
Note: if the quest has fixed rewards that aren't the desired item, it will
reduce the expected count for the desired item. Just subtract the number
of fixed items from the output to get the actual value.
"""
if luck_skill == LUCK_SKILL_NONE:
extend_p = 69
elif luck_skill == LUCK_SKILL_GOOD:
extend_p = 81
elif luck_skill == LUCK_SKILL_GREAT:
extend_p = 90
else:
raise ValueError()
if line == QUEST_A:
min_c = 3
max_c = 8
elif line == QUEST_B:
min_c = 1
max_c = 8
elif line == QUEST_SUB:
min_c = 1
max_c = 4
else:
raise ValueError()
return reward_expected_c(min_c, max_c, extend_p)
def capture_reward_expected_c(cap_skill=CAP_SKILL_NONE):
"""
Expected value for number of capture rewards given the specified
capture skill (none by default).
"""
if cap_skill == CAP_SKILL_NONE:
min_c = 2
max_c = 3
extend_p = 69
elif cap_skill == CAP_SKILL_EXPERT:
return 3
elif cap_skill == CAP_SKILL_MASTER:
min_c = 3
max_c = 4
extend_p = 69
elif cap_skill == CAP_SKILL_GOD:
return 4
else:
raise ValueError()
return reward_expected_c(min_c, max_c, extend_p)
def carve_delta_expected_c(carve_skill):
"""
Expected value for the number of extra carves with the given skill.
Word on the street is that since Tri the felyne skills do not stack with
the armor skills, i.e. if you have Carving Celebrity plus you get at
least one extra carves and the felyne skills do nothing.
"""
if carve_skill == CARVING_SKILL_CELEBRITY:
# Description: Increases the number of carving chances by one and prevents knockbacks while carving.
return 1
elif carve_skill == CARVING_SKILL_GOD:
# Description: Increases the number of carving chances by one (maybe more) and prevents knockbacks while carving.
min_c = 1
max_c = 2
extend_p = 50
elif carve_skill == CARVING_SKILL_FELYNE_LOW:
min_c = 0
max_c = 1
extend_p = 25
elif carve_skill == CARVING_SKILL_FELYNE_HI:
min_c = 0
max_c = 1
extend_p = 50
elif carve_skill in (CARVING_SKILL_NONE, CARVING_SKILL_PRO):
# Description: Prevents knockbacks from attacks while carving.
return 0
else:
raise ValueError()
return reward_expected_c(min_c, max_c, extend_p)
if __name__ == '__main__':
# in percent
reward_percent = int(sys.argv[1])

@ -0,0 +1,151 @@
#!/usr/bin/env python
import mhdb
import mhprob
db = mhdb.MHDB("mh4u.db")
def _format_range(min_v, max_v):
if min_v == max_v:
return "%5.2f%%" % min_v
else:
return "%5.2f%% to %5.2f%%" % (min_v, max_v)
def get_quests(item_name):
"""
Get a list of the quests for acquiring a given item and the probability
of getting the item, depending on cap or kill and luck skills.
"""
item_row = db.get_item_by_name(item_name)
item_id = item_row["_id"]
quests = db.get_item_quests(item_id)
for q in quests:
print q
print " %20s" % "= Quest"
quest_ev = 0
sub_used = False
fixed_other_rewards = dict(A=0, B=0, Sub=0)
total_reward_p = dict(A=0, B=0, Sub=0)
for reward in q._rewards:
slot = reward["reward_slot"]
#reward_item_row = db.get_item(reward["item_id"])
#print slot, reward_item_row["name"], reward["percentage"]
if reward["percentage"] == 100:
if reward["item_id"] != item_id:
fixed_other_rewards[slot] += 1
else:
total_reward_p[slot] += reward["percentage"]
# sanity check values from the db
for slot in total_reward_p.keys():
if total_reward_p[slot] not in (0, 100):
print "WARNING: bad total p for %s = %d" \
% (slot, total_reward_p[slot])
for reward in q._rewards:
slot = reward["reward_slot"]
#reward_item_row = db.get_item(reward["item_id"])
#print slot, reward_item_row["name"], reward["percentage"]
if reward["item_id"] == item_id:
totals = [mhprob.quest_reward_expected_c(slot, skill)
for skill in xrange(mhprob.LUCK_SKILL_NONE,
mhprob.LUCK_SKILL_GREAT+1)]
evs = [((i - fixed_other_rewards[slot])
* reward["stack_size"] * reward["percentage"])
for i in totals]
print " %20s %d %5.2f%%" % (reward["reward_slot"],
reward["stack_size"],
evs[0]),
print " (%2d%% each)" % reward["percentage"],
if len(totals) > 1:
print " %s" % " ".join("%0.2f" % i for i in evs[1:]),
print
quest_ev += evs[0]
if reward["reward_slot"] == "Sub":
sub_used = True
monsters = db.get_quest_monsters(q.id)
cap_ev = [quest_ev, quest_ev]
kill_ev = [quest_ev, quest_ev]
shiny_ev = 0
for m in monsters:
mid = m["monster_id"]
monster = db.get_monster(mid)
print " %20s" % ("= " + monster["name"] + " " + q.rank)
rewards = db.get_monster_rewards(mid, q.rank)
for reward in rewards:
cap = kill = shiny = False
if reward["item_id"] == item_id:
if reward["condition"] == "Body Carve":
totals = [
3 + mhprob.carve_delta_expected_c(skill)
for skill in xrange(mhprob.CARVING_SKILL_PRO,
mhprob.CARVING_SKILL_GOD+1)
]
cap = False
kill = True
elif reward["condition"] == "Tail Carve":
totals = [
1 + mhprob.carve_delta_expected_c(skill)
for skill in xrange(mhprob.CARVING_SKILL_PRO,
mhprob.CARVING_SKILL_GOD+1)
]
cap = kill = True
elif reward["condition"] == "Capture":
totals = [
mhprob.capture_reward_expected_c(skill)
for skill in xrange(mhprob.CAP_SKILL_NONE,
mhprob.CAP_SKILL_GOD+1)
]
cap = True
kill = False
else:
totals = [1]
# don't include Shiny in ev calculations
if reward["condition"].startswith("Shiny"):
cap = kill = False
shiny = True
elif reward["condition"].startswith("Break"):
cap = kill = True
else:
raise ValueError("Unknown condition: "
+ reward["condition"])
evs = [i * reward["stack_size"] * reward["percentage"]
for i in totals]
if cap:
cap_ev[0] += evs[0]
cap_ev[1] += evs[-1]
if kill:
kill_ev[0] += evs[0]
kill_ev[1] += evs[-1]
if shiny:
shiny_ev += evs[0]
print " %20s %d %5.2f%%" % (reward["condition"],
reward["stack_size"],
evs[0]),
print " (%2d%% each)" % reward["percentage"],
if len(totals) > 1:
print " %s" % " ".join("%0.2f" % i for i in evs[1:]),
print
print " %20s" % "= Totals"
print " %20s %s" % \
("Kill", _format_range(*kill_ev))
print " %20s %s" % \
("Cap", _format_range(*cap_ev))
print " %20s %5.2f%%" % ("Shiny", shiny_ev)
print
if __name__ == '__main__':
import sys
get_quests(sys.argv[1])
Loading…
Cancel
Save