【!calciv・!calcev】実数値から個体値・努力値を求める [DiscordBot]

!calciv (個体値表示)

!calciv ポケモン名[*] レベル H A B C D S
!calciv ポケモン名[*] レベル 個体値を調べたい場所(HABCDS) 対応する箇所の実数値

実数値から個体値を計算します。

性格の補正については、上昇補正/下降補正がついている場合は実数値の後に+/-を付けてください。

個体値を調べたい場所を入力しなかった場合は、全ての場所の個体値を求めます。

引数はHABCDSの順で全ての数値を入力してください。

個体値を調べたい場所を入力した場合は、その場所の個体値のみを求めます。

使用例

実数値から個体値を計算するプログラム

概要

実数値(観測値)から個体値を計算するには、!stのときに作ったbst2ast_ij関数を最利用して、

個体値が0~31のときの実数値(理論値)を求め、観測値と一致するような個体値の最小値・最大値を返せばいい。

今回も、H,A,B,C,D,Sをj=1,2,3,4,5,6に対応させて、

j番目の観測値をobst_j,個体値がiのときの理論値をast_ijとする。

l2s=lambda s,l:s.join(map(str,l))
cplist=lambda org:[org,org,org,org,org,org]
#種族値をj番目の実数値に変換
def bst2ast_ij(j,bst,lv,iv,ev,ncor):
    f=lambda bst,iv,ev,lv:int(int(bst*2+iv+ev/4)*lv/100)+5
    if j==0:
        ast_j=f(bst[j],iv[j],ev[j],lv)+lv+5
    else:
        ast_j=int((f(bst[j],iv[j],ev[j],lv))*ncor[j])
    return ast_j

#種族値を実数値リストに変換
def bst2ast(bst,lv,iv,ev,ncor):
    ast_i=[]
    for j in range(6):
        ast_j=bst2ast_ij(j,bst,lv,iv,ev,ncor)
        ast_i.append(ast_j)
    return ast_i

#グラードンの種族値リスト
bst=[100,150,140,100,90,90] 
#デフォルトのLv
lv=50 
 #個体値・努力値・性格補正
b2a_i=[cplist(31),cplist(252),cplist(1.1)]
ast_i=bst2ast(bst,lv,*b2a_i)
print(ast_i)

#出力:[207, 222, 211, 167, 156, 156]
[207, 222, 211, 167, 156, 156]

そのためには、bst2ast_ijの引数である、

種族値努力値・レベル・性格補正、すなわち bst, ev, lv, ncor を決定しなければならない。

  1. bst…与えられた名前をDBを検索して種族値を取得
  2. ev…捕獲直後を想定するため、すべて0を仮定
  3. lv…与えられる。
  4. ncor…与えられる。(実数値の後ろに+,-をつけて表現する。デフォルト=[1,1,1,1,1,1])

入力形式について

!calcivは引数の入力を省略できるので、両方の入力形式に対応する必要がある。

def calciv(name,lv,*args):
    print(name,lv,args)

#省略しない場合
calciv("グラードン",70,"240","236","240+","160","144","117-")

#省略する場合
calciv("グラードン",70,'A','S',"236","117-")
グラードン 70 ('240', '236', '240+', '160', '144', '117-')
グラードン 70 ('A', 'S', '236', '117-')

省略する場合を考えると、次のようなプログラムでどのステータスの個体値を調べたいのかを判定できる。

省略されなかった場合は、すべてFalseになる。

しかし、それだとどこも調べてくれないので、例外的に処理する必要がある。

args=list(('A','S',"236","117-"))

#個体値を調べたいステータスにフラグを立てる
search_st=[st in args for st in ['H','A','B','C','D','S']]
print(search_st)

#Trueの出現回数
print(search_st.count(True))
[False, True, False, False, False, True]
2

argsの "A", "S" の要素はもう必要ないので削除。

del args[:search_st.count(1)]
print(args)
['236', '117-']

次に、args=('236', '117-') について考える。

文字列に、'+', '-' が含まれているかどうかを判定する。

そのために、reモジュールをimportして、特定の文字列があるかどうかを判定する。

ついでに、'+', '-' を除去して、数値型に変換する。

import re

obst=[]
ncor_flag=[]

for arg in args:
    #上昇補正
    if re.search(r'\+',arg):
        arg=arg.strip('+')
        ncor_flag.append(1.1)
    #下降補正
    if re.search(r'\-',arg):
        arg=arg.strip('-')
        ncor_flag.append(0.9)
    #無補正
    else:
        ncor_flag.append(1)
    obst.append(int(arg))

print(obst)
print(ncor_flag)
[236, 117]
[1, 0.9]

これで、bst2ast_ijが使えるようになった。

#arg='117-'をobst_j=117, ncor_j=0.9に分離する関数
def args2ast_j(arg):
    #上昇補正
    if re.search(r'\+',arg):
        ast_j=arg.strip('+')
        ncor_j=1.1
    #下降補正
    elif re.search(r'\-',arg):
        ast_j=arg.strip('-')
        ncor_j=0.9
    #無補正
    else:
        ast_j=arg
        ncor_j=1
    return int(ast_j),ncor_j

def calciv(name,lv,*args):
    args=list(args)
    #個体値を調べたい場所にフラグを立てる
    search_st=[st in args for st in ['H','A','B','C','D','S']]
    del args[:search_st.count(1)]
        
    iter_args=iter(args)
    #観測したステータス
    obst=[]
    ncor=[]
    for j in range(6):
        #フラグがあるか、ひとつもない場合
        if search_st[j] or search_st.count(True)==0:
            arg=next(iter_args)
            obst_j,ncor_j=args2ast_j(arg)
            obst.append(obst_j)
            ncor.append(ncor_j)
        #フラグがない場合
        else:
            obst.append(0)
            ncor.append(1)

    bst=(100, 150, 140, 100, 90, 90)
    lv=lv
    ev=cplist(0)
    i_maxmindict={}
    i_matchdict={}
    #H~Sでループ
    for j in range(6):
        if search_st[j] or search_st.count(True)==0:
            i_matchdict[j]=[]
            #個体値0~31でループ
            for i in range(32):
                iv=cplist(i)
                #実数値が一致したときの個体値
                if obst[j]==bst2ast_ij(j,bst,lv,iv,ev,ncor):
                    i_matchdict[j].append(i)
            if len(i_matchdict[j])==0:
                pass
            elif len(i_matchdict[j])==1:
                i_maxmindict[j]=i_matchdict[j]
            else:
                i_maxmindict[j]=[min(i_matchdict[j]),max(i_matchdict[j])]

    keys=['H','A','B','C','D','S']
    for item in i_maxmindict.items():
        print(f"{keys[item[0]]}:{l2s('~',item[1])}")
#省略しない場合
calciv("グラードン*",69,"240","236","240+","160","144","117-")
B:31
C:25~26
D:22
S:2~4
#省略する場合
calciv("グラードン",70,'A','S',"236","117-")
A:30~31
S:0~1

これでほとんど完成!

PostgreSQLから種族値データを取得する

DBの検索には、!stのときのPostgreSQLから種族値データを取得する関数を再利用する

import psycopg2,os,re
import numpy as np
from dotenv import load_dotenv

DATABASE_URL=os.environ["DATABASE_URL"]
conn=psycopg2.connect(DATABASE_URL)
l2s=lambda s,l:s.join(map(str,l))
#種族値データを取得
def fetch_bst(cur,name):
    #アイコンを生成
    cur.execute(
        f"select icon from pokedex where name='{name}'"
        )
    icon=cur.fetchone()[0]
    #種族値を検索
    cur.execute(
        f"select h,a,b,c,d,s from pokedex where name ='{name}'"
        )
    bst=cur.fetchone()
    return icon,bst

#初めに呼び出される関数
def get_bst(name):
    with conn:
        with conn.cursor() as cur:
            #名前の最後に"*"をつけるとあいまい検索になる
            #あいまい検索している場合
            if re.findall(r'\*',name):
                name=name.strip('\*')
                #DBから名前をあいまい検索
                cur.execute(
                    f"""
                    select name from pokedex
                    where name like '{name}%' order by id
                      """
                    )
                #ヒットしたすべての結果:fnames
                fnames=np.array(cur.fetchall()).flatten()
                for fname in fnames:
                    icon,bst=fetch_bst(cur,fname)
                    print(bst)
            #あいまい検索でない場合
            else:
                icon,bst=fetch_bst(cur,name)
                print(bst)

#'*'なし、普通の検索
get_bst("グラードン")
(100, 150, 140, 100, 90, 90)

↑これでグラードン種族値が取得できた。

!calcev(努力値計算)

!calcev ポケモン名[*] レベル H A B C D S
!calcev ポケモン名[*] レベル 努値を調べたい場所(HABCDS) 対応する箇所の実数値

個体値を31と仮定して、実数値から努力値を計算します。

性格の補正については、上昇補正/下降補正がついている場合は実数値の後に+/-を付けてください。

努力値を調べたい場所を入力しなかった場合は、全ての場所の努力値を求めます。

努力値を調べたい場所を入力した場合は、その場所のみを求めます。

使用例

個体値が31でない場合は、努力値を0と仮定して、個: [個体値] の形式で出力される

実数値から努力値を計算するプログラム

概要

実は、calcivの条件をほんの少し変えるだけで作れる。

個体値 努力値
calciv 不明 0と仮定
calcev 31と仮定 不明
#calcev
name="グラードン"
lv=100
iv=cplist(31)
ast=[341,399,316,200,216,306]
ncor=[1,1,1,0.9,1,1.1]
i_maxmindict={}
i_matchdict={}
#H~Sでループ
for j in range(6):
    i_matchdict[j]=[]
    #努力値0~252でループ(0,4,12,20...)
    for i in range(64):
        ev_i=4*i
        ev=cplist(ev_i)
        #実数値が一致したときの努力値
        if ast[j]==bst2ast_ij(j,bst,lv,iv,ev,ncor):
            i_matchdict[j].append(ev_i)
    if len(i_matchdict[j])==0:
        pass
    elif len(i_matchdict[j])==1:
        i_maxmindict[j]=i_matchdict[j]
    else:
        i_maxmindict[j]=[min(i_matchdict[j]),max(i_matchdict[j])]

keys=['H','A','B','C','D','S']
for item in i_maxmindict.items():
    print(f"{keys[item[0]]}:{l2s('~',item[1])}")
H:0
A:252
B:0
D:0
S:252

個体値が31より低い場合

個体値が31より低い場合は、努力値を0と仮定し、calciv_jを呼び出して、個体値を求めることにする。

例:Lv100 グラードン 陽気 の特攻が200だったとき、とりうる個体値

def calciv_j(ast_j,j,bst,lv,ncor):
    ev=cplist(0)
    #個体値0~31でループ
    i_matchdict_j=[]
    for i in range(32):
        iv=cplist(i)
        #実数値が一致したときの個体値
        if ast_j==bst2ast_ij(j,bst,lv,iv,ev,ncor):
            i_matchdict_j.append(i)
    if len(i_matchdict_j)==0:
        pass
    elif len(i_matchdict_j)==1:
        i_maxmin_j=str(i_matchdict_j[0])
    else:
        i_maxmin_j=f"{min(i_matchdict_j)}~{max(i_matchdict_j)}"
    print(i_maxmin_j)

name="グラードン"
ast_j=200
j=3
bst=[100,150,140,100,90,90]
lv=100
ncor=[1,1,1,0.9,1,1.1]
print("C個体値")
calciv_j(ast_j,j,bst,lv,ncor)
C個体値
18

あとはプログラムをdiscord.pyの形式に修正し、出力形式をembedにすればOK!