--- /dev/null
+# -*- coding: utf-8 -*-
+
+u"""
+錬金のシミュレータ
+2013/12/19 written by kei9
+"""
+
+# import modules
+import sqlite3
+
+import mh4constnumbers
+from randomgenerator import RandomGenerator
+from skillminmaxtable import SkillMinMaxTableAccessor
+from skilltable import SkillTableAccessor
+from amulettable import AmuletTableAccessor
+from sufficienttable import SufficientTableAccessor
+import singleton
+
+class AlchemySimulator(object):
+ u""" 特定Seed1,2から生じる錬金結果を模擬するクラス """
+ __metaclass__ = singleton.Singleton
+ def __init__(self, db_cursor):
+ u""" 錬金結果の模擬クラス """
+ self._cursor = db_cursor
+ acc_minmax = SkillMinMaxTableAccessor(db_cursor)
+ acc_skill = SkillTableAccessor(db_cursor)
+ acc_amulet = AmuletTableAccessor(db_cursor)
+ self._acc_suff = SufficientTableAccessor(db_cursor)
+ self._skill_id2name, self._skill_name2id = acc_skill.get_dict()
+ self._amu_id2name, self._amu_name2id = acc_amulet.get_dict()
+ self._minmax_dict = acc_minmax.get_minmax_by_id()
+
+ def _get_skill_value(self, rand1, rand2, skill_max, skill_min):
+ u""" 与えられた2つの乱数値とスキルの最大、最小値からスキル値を計算する """
+ value = (rand1 ^ rand2) % (skill_max - skill_min + 1) + skill_min # XOR
+ return value
+
+ def alchemy_nazo_id(self, seed1, seed2):
+ u""" なぞの錬金
+ return (skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name)
+ """
+ gen_c = RandomGenerator(seed1) # generator C
+ gen_d = RandomGenerator(seed2) # generator D
+ return self._judge(gen_c, gen_d, mh4constnumbers.NAME_AMULET0) # nazo
+
+ def alchemy_nazo_name(self, seed1, seed2):
+ u""" なぞの錬金
+ return (skill1_name, skill1_val, skill2_name, skill2_val, slot_num, type_name)
+ """
+ val = self.alchemy_nazo_id(seed1, seed2)
+ skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name = val
+ if skill2_id in self._skill_id2name:
+ return (self._skill_id2name[skill1_id], skill1_val,
+ self._skill_id2name[skill2_id], skill2_val, slot_num, type_name)
+ else:
+ return (self._skill_id2name[skill1_id], skill1_val, u"", 0, slot_num, type_name)
+
+ def alchemy_komyou_id(self, seed1, seed2):
+ u""" 光明の錬金
+ return (skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name)
+ """
+ gen_c = RandomGenerator(seed1) # generator C
+ gen_d = RandomGenerator(seed2) # generator D
+ return self._judge(gen_c, gen_d, mh4constnumbers.NAME_AMULET1) # komyou
+
+ def alchemy_komyou_name(self, seed1, seed2):
+ u""" 光明の錬金
+ return (skill1_name, skill1_val, skill2_name, skill2_val, slot_num, type_name)
+ """
+ val = self.alchemy_komyou_id(seed1, seed2)
+ skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name = val
+ if skill2_id in self._skill_id2name:
+ return (self._skill_id2name[skill1_id], skill1_val,
+ self._skill_id2name[skill2_id], skill2_val, slot_num, type_name)
+ else:
+ return (self._skill_id2name[skill1_id], skill1_val, u"", 0, slot_num, type_name)
+
+ def alchemy_inishie_id(self, seed1, seed2):
+ u""" いにしえの錬金
+ return (skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name)
+ """
+ gen_c = RandomGenerator(seed1) # generator C
+ gen_d = RandomGenerator(seed2) # generator D
+ return self._judge(gen_c, gen_d, mh4constnumbers.NAME_AMULET2) # inishie
+
+ def alchemy_inishie_name(self, seed1, seed2):
+ u""" いにしえの錬金
+ return (skill1_name, skill1_val, skill2_name, skill2_val, slot_num, type_name)
+ """
+ val = self.alchemy_inishie_id(seed1, seed2)
+ skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name = val
+ if skill2_id in self._skill_id2name:
+ return (self._skill_id2name[skill1_id], skill1_val,
+ self._skill_id2name[skill2_id], skill2_val, slot_num, type_name)
+ else:
+ return (self._skill_id2name[skill1_id], skill1_val, u"", 0, slot_num, type_name)
+
+ def alchemy_tenun_id(self, seed1, seed2, alchemy_type):
+ u""" 天運の錬金 (古*3または歪*3限定。)
+ return list of (skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name)
+ """
+ if alchemy_type == mh4constnumbers.KEY_TENUN555:
+ result_num_max = mh4constnumbers.TENUN555_MAX
+ result_num_min = mh4constnumbers.TENUN555_MIN
+ yuga_divisor = mh4constnumbers.TENUN555_DIVISOR
+ elif alchemy_type == mh4constnumbers.KEY_TENUN888:
+ result_num_max = mh4constnumbers.TENUN888_MAX
+ result_num_min = mh4constnumbers.TENUN888_MIN
+ yuga_divisor = mh4constnumbers.TENUN888_DIVISOR
+ else:
+ raise NotImplementedError(u"Irregular Alchemy Type!")
+
+ gen_a = RandomGenerator(seed1) # generator A
+ gen_b = RandomGenerator(seed2) # generator B
+
+ result_num = gen_a.get_next() % (result_num_max - result_num_min + 1) + result_num_min
+ result_list = []
+
+ for i in range(result_num):
+ s1 = gen_a.get_next()
+ s2 = gen_b.get_next()
+ amulet_type = s1 % yuga_divisor
+ if 0 <= amulet_type < 10:
+ u""" 謎のお守り """
+ amulet_name = mh4constnumbers.NAME_AMULET0
+ elif 10 <= amulet_type < 55:
+ u""" 光るお守り """
+ amulet_name = mh4constnumbers.NAME_AMULET1
+ elif 55 <= amulet_type < 85:
+ u""" 古びたお守り """
+ amulet_name = mh4constnumbers.NAME_AMULET2
+ elif 85 <= amulet_type < 100:
+ u""" 歪んだお守り """
+ amulet_name = mh4constnumbers.NAME_AMULET3
+ else:
+ raise NotImplementedError(u"Divisor must be incorrect")
+
+ gen_c = RandomGenerator(s1) # generator C
+ gen_d = RandomGenerator(s2) # generator D
+
+ result_list.append(self._judge(gen_c, gen_d, amulet_name))
+ return result_list
+
+ def alchemy_tenun_name(self, seed1, seed2, alchemy_type):
+ u""" 天運の錬金(古*3または歪*3限定。)
+ return list of (skill1_name, skill1_val, skill2_name, skill2_val, slot_num, type_name)
+ """
+ result_list = []
+ result = self.alchemy_tenun_id(seed1, seed2, alchemy_type)
+ for skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name in result:
+ if skill2_id in self._skill_id2name:
+ result_list.append(
+ (self._skill_id2name[skill1_id], skill1_val,
+ self._skill_id2name[skill2_id], skill2_val, slot_num, type_name))
+ else:
+ result_list.append((self._skill_id2name[skill1_id], skill1_val, u"", 0, slot_num, type_name))
+
+ return result_list
+
+ def _judge(self, gen_c, gen_d, amulet_name):
+ u""" 乱数生成器C,Dおよびお守りの種類から鑑定を行う
+ return (skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name)
+ """
+ amulet_id = self._amu_name2id[amulet_name]
+ skill1_minmax, skill2_minmax = self._minmax_dict[amulet_id]
+ s1 = gen_c.get_next() # for skill1 dicision
+ s1 %= len(skill1_minmax) # skill1_pos
+ skill1_id = [x for i,x in enumerate(sorted(skill1_minmax.keys())) if i == s1][0]
+ min1, max1 = skill1_minmax[skill1_id]
+
+ x1 = gen_c.get_next()
+ x2 = gen_d.get_next()
+ skill1_val = (x1 ^ x2) % (max1 - min1 + 1) + min1 # XOR
+
+ y1 = gen_c.get_next()
+ y2 = gen_d.get_next()
+ border = mh4constnumbers.SKILL2_BORDER_DICT[amulet_name]
+
+ if (y1 ^ y2) % 100 >= border: # decide skill2 to exist
+ s2 = gen_d.get_next() # for skill2 dicision
+ s2 %= len(skill2_minmax) # skill2_pos
+ skill2_id = [x for i,x in enumerate(sorted(skill2_minmax.keys())) if i == s2][0]
+ min2, max2 = skill2_minmax[skill2_id]
+
+ if min2 < 0:
+ z0 = gen_c.get_next()
+ else:
+ z0 = None
+
+ z1 = gen_c.get_next()
+ z2 = gen_d.get_next()
+ if min2 < 0 and z0 % 2 == 0: # skill2 val is minus
+ skill2_val = (z1 ^ z2) % (1 - min2) + min2
+ else: # skill2 val is plus
+ skill2_val = (z1 ^ z2) % max2 + 1
+
+ if skill2_id == skill1_id or skill2_val == 0:
+ skill2_id = None
+ skill2_val = 0
+ else:
+ skill2_id = None
+ skill2_val = 0
+
+ # slot number
+ if skill2_val <= 0:
+ suff_val = (skill1_val * 10) // max1
+ else:
+ suff_val = (skill1_val * 10) // max1 + (skill2_val * 10) // max2
+
+ w = gen_c.get_next()
+ if (w & mh4constnumbers.SEED_DECIDE_FLAG) != 0: # falg = 128, 50%
+ w2 = gen_c.get_next()
+ else:
+ w2 = gen_d.get_next()
+ _slot = w2 % 100
+
+ th1_slot, th2_slot, th3_slot = self._acc_suff.select_thresholds_by_id(amulet_id, suff_val)
+ if _slot < th1_slot:
+ slot_num = 0
+ slot_point = mh4constnumbers.SLOT_POINT0
+ elif th1_slot <= _slot < th2_slot:
+ slot_num = 1
+ slot_point = mh4constnumbers.SLOT_POINT1
+ elif th2_slot <= _slot < th3_slot:
+ slot_num = 2
+ slot_point = mh4constnumbers.SLOT_POINT2
+ elif th3_slot <= _slot:
+ slot_num = 3
+ slot_point = mh4constnumbers.SLOT_POINT3
+
+ type_name = self._decide_amulet_type(suff_val, slot_point, amulet_name)
+
+ return (skill1_id, skill1_val, skill2_id, skill2_val, slot_num, type_name)
+
+
+ def _decide_amulet_type(self, suff_val, slot_point, amulet_name):
+ u""" 鑑定結果の護石名を充足値、スロットポイント、お守り名から決定する """
+ type_val = suff_val + slot_point
+
+ border1, border2, border3 = mh4constnumbers.TYPE_BORDER_DICT[amulet_name]
+ if type_val >= border3[0]:
+ return border3[1]
+ elif type_val >= border2[0]:
+ return border2[1]
+ elif type_val >= border1[0]: # 0
+ return border1[1]
+ else:
+ return None