乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

シミュレーター

今回は金融庁の
NISA積立シミュレーターがやばいので
その解説です。


解説動画はこちら


 

金融庁の積立シミュレーター

これがそのシミュレーターです。

サイト

ここで
毎月の積立1万円
年利12%
40年間

で試算してみます。
スクリーンショット 2025-06-14 17.12.47


こんな感じになりました。

年利が高すぎますが
計算がわかりやすいようにして
計算してみた結果です。

40年後の資産は
11765万円になりました



Pythonでのシミュレーションコード


先程のシミュレーターと同じ計算を
Pythonでも計算してみましょう。

シミュレーションコード

monthly_rate = 0.12 / 12       # 月利(年利12%)
months = 40 * 12               # 総月数(480ヶ月)
monthly_payment = 10000       # 毎月の積立額(マイナス表記にする)

total = 0
for _ in range(months):
    total = total * (1 + monthly_rate) + monthly_payment

print(f"将来の資産総額: {total:,.0f} 円")
将来の資産総額: 117,647,725 円


金融庁のシミュレーターの結果は
11765万円 なので
ほぼ同じ結果になりました。


落とし穴

実はここに落とし穴があります!!!!

年利計算での正しいシミュレーション結果は
こうなります。
(年末に積立するパターン)

year_rate = 0.12            # 年利12%
years = 40                  # 総年数(40年)
year_payment = 120000    # 年の積立額

total = 0
for _ in range(years):
    total = total * (1 + year_rate) + year_payment

print(f"将来の資産総額: {total:,.0f} 円")
将来の資産総額: 92,050,970 円

先程の計算結果 : 117,647,725 円
とは大きく乖離してきます。


からくり

金融庁のサイトのシミュレーションは年利ではなく
年利を12ヶ月で割った月利の複利(年利 / 12)で計算しているようです。

そのため年利と月利では、計算結果が大きく異なってきます。

もし月利複利計算を正しくするとしたら
12乗して年利になるような値を月利にしないといけません。

月利 = (1 + 0.12) ** (1/12) - 1
= 0.009488792934583046 = 0.948%

年利から月利に直して計算するコードはこちら
monthly_rate = (1 + 0.12) ** (1/12) - 1  # 月利(年利12%)
months = 40 * 12                          # 総月数(480ヶ月)
monthly_payment = 10000                 # 毎月の積立額(マイナス表記にする)

total = 0
for _ in range(months):
    total = total * (1 + monthly_rate) + monthly_payment

print(f"将来の資産総額: {total:,.0f} 円")
将来の資産総額: 97,010,200 円

月利計算だとこのくらいの金額になります。




資産推移のシミュレーター

年利を月利換算しても正しく計算するようにしたものは
こんな感じになります。
import pandas as pd
import plotly.express as px

def plot_investment(rate_percent, years, payment_man):
    annual_rate = rate_percent / 100
    monthly_rate = (1 + annual_rate) ** (1/12) - 1
    months = years * 12
    monthly_payment = payment_man * 10000

    results, ci_total, am_total = [], 0, 0
    for month in range(1, months + 1):
        ci_total = ci_total * (1 + monthly_rate) + monthly_payment
        am_total += monthly_payment
        results.append({'月': month, '資産総額': ci_total, '積立金額': am_total})

    df = pd.DataFrame(results)
    fig = px.line(df, x='月', y=['資産総額', '積立金額'],
                  labels={'value': '金額(円)', 'variable': '項目'},
                  title=f'毎月{payment_man}万円積立(年利{rate_percent:.1f}%・{years}年)の資産推移')
    
    fig.update_layout(
        yaxis_tickformat=',',
        height=400
    )
    fig.show()

積立シミュレーション設定

#@title 積立シミュレーション設定
rate_percent = 5.1 # @param {type:"slider", min:1.0, max:20.0, step:0.1}
years = 30 # @param {type:"slider", min:10, max:50, step:1}
payment_man = 20 # @param {type:"slider", min:1, max:30, step:1}

plot_investment(rate_percent, years, payment_man)

colabで実行するとこのような画面が出てきます
スクリーンショット 2025-06-14 17.34.43

これを設定して実行すると
スクリーンショット 2025-06-14 17.34.53
積立のシミュレーション結果が反映されます。

colabで試せるので
コピペして試してみてください。



まとめ

金融庁のサイトのシミュレーターは
年利と月利を齟齬しているので
数十年後の金額が大きく乖離してくるようです。

単利と複利、月利と年利
この辺りの関係を正しく計算できるように
日々プログラムでシミュレーションすると安全です。

資産形成に取り組みたい方は
この機会にプログラミングも併せて
学んでみてはいかがでしょうか?


それでは。


最近、積立NISAなるものが
流行ってるみたいなので
シミュレーターを作ってみました。


解説動画はこちら



そもそも、積立NISAとは?
 
2018年1月より開始された
少額投資非課税制度のことで
非課税投資枠が年間40万円で
投資期間が最長20年になります

詳細はこちらをみていただければと思います

金融庁・積立NISA

今回は金融庁が出している
シミュレーターを真似して作ってみました

金融庁・資産運用シミュレーション

こんな感じのをPythonで作ってみます

コードはこんな感じです

from matplotlib import pyplot as plt
import numpy as np
import ipywidgets as widgets
from IPython.display import display
import warnings
warnings.simplefilter('ignore')

# 積立NISAシミュレーター
def show_widgets():
    button = widgets.Button(description='シミュレーション')
    s1 = widgets.IntSlider(value=20000  , min=1000 , max=33000  , step=1000   , description='月の積立金:')
    s2 = widgets.FloatSlider(value=4.0, min=0.1, step=0.1, max=10.0, description='利率:')
    s3 = widgets.IntSlider(value=10  , min=1 , max=20  , step=1 , description='年数:')
    output0 = widgets.Output(layour={'border': '1px solid black'})
    output1 = widgets.Output()
    fig = plt.figure(figsize=(12,6))
    ax = plt.gca()
    
    # クリック時の実行
    def on_click(b: widgets.Button) -> None:
        ax.clear()
        reserve = s1.value
        rate = s2.value
        year = s3.value
        rand_y1,rand_y2 = [],[]
        money,cost = 0,0
        for i in range(1,year+1):
            money += reserve * 12
            cost += reserve * 12
            money = int(money*(1+rate/100))
            diff = money - cost
            rand_y1.append(cost)
            rand_y2.append(diff)
        
        # 棒グラフの描画
        x = list(range(1,len(rand_y1)+1))        
        p1=ax.bar(x , rand_y1)
        p2=ax.bar(x , rand_y2 , bottom=rand_y1)
        l1=list(map(lambda x:'{0:.1f}万円'.format(x/10000), rand_y1))
        l2=list(map(lambda x:'{0:.1f}万円'.format(x/10000), rand_y2))
        l3=list(map(lambda x:'{0:.1f}万円'.format((x[0]+x[1])/10000), zip(rand_y1,rand_y2)))
        # 棒グラフのテキストラベル
        for i in range(len(x)):
            if len(x)<=10:
                size = 10
            elif len(x)<=15:
                size = 8
            elif len(x)<=20:
                size = 7
            else:
                size = 5
            
            ax.text(i +1, rand_y1[i]/2, l1[i], ha='center', color='black',size =size)
            ax.text(i +1, rand_y1[i] + rand_y2[i]/2, l2[i], ha='center', color='black',size =size)
            ax.text(i +1, rand_y1[i] + rand_y2[i] + 20000 , l3[i], ha='center', color='orange',size =size)
        ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, loc: "{:,}".format(int(x))))
        
        with output0:
            output0.clear_output(wait=True)
            out_text = '積立金額 : {0:_>9}円 \n合計利息 : {1:_>9}円 \n最終金額 : {2:_>9}円'
            out = out_text.format(cost , diff , money)
            print(out)
        with output1:
            output1.clear_output(wait=True)
            display(ax.figure)
             
    button.on_click(on_click)
    box1 = widgets.Box([s1,s2])
    box2 = widgets.Box([s3,button])
    display(box1 , box2 , output0 , output1)
    plt.close()
    button.click()
     
show_widgets()

一応GoogleColabでも動くと思うので
試したい方はコピペして
動かしてみてください

ただし、棒グラフの万円の漢字部分が
表示されないだろうと思うので
ご了承下さい

コードのポイントとしては
月の積立金
利率
年数

スライダーで可変にして
これを入力として受け取ります

最終結果は年数分の利息を計算して
積立額にプラスして
最終結果と棒グラフに表示させます

年間40万円までなので
月額は33千円程度が限界ですかね?

これで利率4%だと
10、20年でこうなります。

スクリーンショット 2022-10-15 16.58.26
スクリーンショット 2022-10-15 16.59.18

10年だと積立金額の1.25倍
20年だと積立金額の1.5倍
くらいにはなりそうですね

とはいえ
この制度は始まったばかり
20年後に残っている保証も無いです

毎年必ず4%の利息が貰える
保証もありません

特に今は投資関連が
戦争やら円安やらで
ゲロやばそうな状況ですね

投資をするなら
自己責任でお願いいたします

今日はここまでです
それでは


今回は今話題のFIREについて
検証用のプログラムを作ってみました。

解説動画はこちら




三度の飯よりお金が好きな
乙pyです。

今話題のFIREとは?

Financial Independence , Retire Early
経済的自立と早期リタイアという
訳になると思いますが
資産を手に入れて経済的な自立を
手に入れようというものです。


FIREの目安

月々の生活費の25倍の資産を持つのが目安で
利率4%(5%運用で1%税引き)で運用すれば
生活費=運用益なので資産が減らなくなります。

そうすれば働かなくても
生活費を賄うことができます。

今回はFIREをしたいなと思いましたので
シミュレーターで検証してみましょう。

プログラムはGoogle Colabなどで
利用できるので試してみて下さい。
from matplotlib import pyplot as plt
import numpy as np
import ipywidgets as widgets
from IPython.display import display

out_text = '最終金額 : {0:,} ,年間生活費 : {1:,} \n年間利息 : {2:,} , FIRE倍率 : {3}\n'

# FIREシミュレーター
def show_widgets():
    button = widgets.Button(description='シミュレーション')
    s0 = widgets.IntSlider(value=200000  , min=10000  , max=1000000, step=10000 , description='月の生活費:')
    s1 = widgets.IntSlider(value=2000000, min=0 , max=10000000, step=100000 , description='初期金額:')
    s2 = widgets.IntSlider(value=200000  , min=0 , max=1000000  , step=10000   , description='月の積立金:')
    s3 = widgets.FloatSlider(value=4.0, min=0.1, step=0.1, max=20.0, description='利率:')
    s4 = widgets.IntSlider(value=10  , min=1 , max=50  , step=1 , description='年数:')
    output0 = widgets.Output(layour={'border': '1px solid black'})
    output1 = widgets.Output()
    ax = plt.gca()
    
    def on_click(b: widgets.Button) -> None:
        ax.clear()
        cost = s0.value
        initial = s1.value
        reserve = s2.value
        rate = s3.value
        year = s4.value
        rand_x = []
        money = initial
        for i in range(1,year):
            money += reserve * 12
            money = int(money*(1+rate/100))
            rand_x.append(money)
        f_rate = money / (cost * 12)
        result = '{0:.4}倍'.format(f_rate)
        Interest = int(money*(rate/100))
        ax.plot(list(range(1,len(rand_x)+1)),rand_x)
        ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, loc: "{:,}".format(int(x))))
        with output0:
            output0.clear_output(wait=True)
            out = out_text.format(money, cost * 12 , Interest , result)
            if f_rate>25:
              out += 'FIRE達成しました!!'
            print(out)
        with output1:
            output1.clear_output(wait=True)
            display(ax.figure)
            
    button.on_click(on_click)
    box1 = widgets.Box([s0,s1])
    box2 = widgets.Box([s2,s3])
    box3 = widgets.Box([s4,button])
    display(box1 , box2 , box3 , output0,output1)
    plt.close()
    button.click()
    
show_widgets()

実行すると


スクリーンショット 2022-02-19 16.17.10

こんな感じでスライダーが出てきます。

月の生活費
初期金額
月の積立金
利率
年数
これを変えてシミュレーションボタンを押すと
結果が下にでます。

最終金額
年間生活費 
年間利息
FIRE倍率
これが出ます。

FIRE倍率25倍を越すと達成の表示が出ます。

これを使う際は
生活費や積み立て金を決めて何年経つと
資産がどれくらいになるか・とか

XX年でFIREするには積み立てや利率を
どうすれば良いか・・みたいなのを
シミュレーションできますんで
色々変えて遊んでみて下さい。

手取りで40万くらいないと
月々20万円を運用に回すの厳しいなー
こりゃあしんどい!!!!

というわけで
FIRE結構大変そうですね!
是非遊んでみて下さい

それでは。

前回作成したドラクエ1風のシミュレーターを改造して
ドラクエ2風のチーム戦が出来るようにしました。

解説動画はこちら



さて前回のプログラムを改造して行きましょう。

前回は1vs1だったので
複数vs複数に対応するように改造します。

前回の奴をそのまま使用するのと改造するもの
新規のものといろいろ出てきますが

まずは前回の流用。
キャラクタークラスはそのまま流用できますが
チーム戦になると生存フラグが必要になってきますので
フラグを追加します、1が生存0が死亡。

ダメージ計算や素早さ計算はそのまま流用します。
import numpy as np

# キャラクタークラス
class character():

    def __init__(self,name,hp,attack,defence,agility):
        self.name = name
        self.hp = hp
        self.attack = attack
        self.defence = defence
        self.agility = agility
        self.survive = 1

# ダメージ計算
def damage_calc(c1,c2):
    base = c1.attack //2 - c2.defence//4
    rand = base//16+1
    tmp = np.random.randint(0,rand*2+1)-rand
    damage = base + tmp
    return damage

# 素早さ計算
def agility_calc(c):
    base = c.agility
    tmp = base//10
    result = np.random.randint(base-tmp,base+tmp+1)
    return result

チーム戦を行うために、キャラを複数格納できる
チームクラスを作ります。
# チームクラス
class Team():

    def __init__(self,name,teams):
        self.name = name
        self.team = teams

    def append(self,chara):
        self.team.append(chara)
        
    def delete(self,index):
        del self.team[index]


前回は2キャラの対戦なのでチームでの複数対戦だと
行動の順番を決める必要があります。

行動順番は素早さを元に計算し
数値が大きい順に行動するようにします。

その際生存フラグが1のものだけにします。

# 先頭順序の計算
def turn_calc(t1,t2):
    result = {}
    for i,c in enumerate(t1.team):
        ag = agility_calc(c)
        if c.survive==1:
            result['{0}:{1}'.format(t1.name,i)] = ag
    for i,c in enumerate(t2.team):
        ag = agility_calc(c)
        if c.survive==1:
            result['{0}:{1}'.format(t2.name,i)] = ag
    return [k for k,v in sorted(result.items(),reverse=True,key=lambda x:x[1])]


戦闘順番が決まったらその順番通りに攻撃開始です。
相手チームの中で狙う相手を決めるために
相手チームの生存者を探してインデックス値で返します。

ターン中の攻撃は攻撃者からターゲットへの
一方的なやりとりだけにします。

お互いのステータスからダメージ計算し
相手のHPから引き、HPが0になったら倒れます。

# 狙う相手を計算
def target(teams):
    index = [i for i ,c in enumerate(teams.team) if c.survive==1]
    return np.random.choice(index)

# ターン中の攻撃
def attack(c1,c2):
    damage = damage_calc(c1,c2)
    c2.hp = c2.hp - damage if c2.hp - damage>=0 else 0
    print('{0}の攻撃 : {1}に{2}のダメージ'.format(c1.name,c2.name,damage))
    print('{0}のHP : {1}'.format(c2.name,c2.hp))
    if c2.hp<=0:
        print('{0}は力尽きた!!!'.format(c2.name))
        return (c2,0)
    return (c2,9)

チームの全員の生存フラグが0になったら試合終了です。
全員の生存フラグをチェックします。

ターンが終わったら全員のステータスを出すように
ログ出力します。

# 生存チェック
def check_survived(teams):
    res = 0
    for c in teams.team:
        if c.survive==1:
            res+=1
    if res > 0:
        return True
    else:
        return False

# ログ出力
def print_log(t1,t2):
    print()
    result = ''
    for c in t1.team + t2.team:
        result += '{0} , HP : {1}\t'.format(c.name , c.hp )
    print(result)
    print()

ここから戦闘中のアルゴリズムです。
順番を決めて
攻撃対象を決めて
ダメージ計算して
HP0なら生存フラグを0にして
全員生存フラグ0なら勝負ありです。

# 戦闘
def battle(t1,t2):
    print('戦闘開始')
    print_log(t1,t2)
    while True:
        # ターン内の処理
        for m in turn_calc(t1,t2):
            tm , i , flg= m.split(':')[0] , int(m.split(':')[1]),9
            # 攻撃チームの設定
            attack_team = t1 if t1.name==tm else t2
            # ターゲットチームとターゲットの指定
            target_team = t2 if t1.name==tm else t1
            t_num = target(target_team) 
            # 戦闘結果
            target_team.team[t_num],flg = attack(attack_team.team[i],target_team.team[t_num])
            if flg==0:
                target_team.team[t_num].survive=0
            if not check_survived(t1):
                print('\n{0}チームの勝ち'.format(t2.name))
                break
            elif  not check_survived(t2):
                print('\n{0}チームの勝ち'.format(t1.name))
                break
        
        # ターン後
        if not check_survived(t1) or not check_survived(t2): 
            break
        print_log(t1,t2)
    print('戦闘終了')


さて、シミュレーションしてみましょう。
c1 = character('勇者1',100,80,40,66)
c2 = character('勇者2',100,80,40,66)
c3 = character('勇者3',100,80,40,66)
c4 = character('勇者4',200,100,50,66)

s1 = character('スライム1',180,80,30,66)
s2 = character('スライム2',180,80,30,66)
s3 = character('スライム3',180,80,30,66)
s4 = character('スライム4',380,70,40,56)

team1 = Team('勇者チーム',[c1,c2,c3,c4])
team2 = Team('スライムチーム',[s1,s2,s3])

battle(team1,team2)

結果は

戦闘開始

勇者1 , HP : 100 勇者2 , HP : 100 勇者3 , HP : 100 勇者4 , HP : 200
スライム1 , HP : 180 スライム2 , HP : 180 スライム3 , HP : 180

勇者1の攻撃 : スライム2に36のダメージ
スライム2のHP : 144
勇者2の攻撃 : スライム3に34のダメージ
スライム3のHP : 146
スライム3の攻撃 : 勇者3に29のダメージ
勇者3のHP : 71
スライム1の攻撃 : 勇者3に28のダメージ
勇者3のHP : 43
勇者4の攻撃 : スライム2に41のダメージ
スライム2のHP : 103
勇者3の攻撃 : スライム3に36のダメージ
スライム3のHP : 110
スライム2の攻撃 : 勇者4に30のダメージ
勇者4のHP : 170

勇者1 , HP : 100 勇者2 , HP : 100 勇者3 , HP : 43 勇者4 , HP : 170
スライム1 , HP : 180 スライム2 , HP : 103 スライム3 , HP : 110

スライム1の攻撃 : 勇者4に29のダメージ
勇者4のHP : 141
勇者4の攻撃 : スライム2に43のダメージ
スライム2のHP : 60
勇者1の攻撃 : スライム2に32のダメージ
スライム2のHP : 28
勇者2の攻撃 : スライム2に31のダメージ
スライム2のHP : 0
スライム2は力尽きた!!!
スライム3の攻撃 : 勇者4に27のダメージ
勇者4のHP : 114
勇者3の攻撃 : スライム3に30のダメージ
スライム3のHP : 80
スライム2の攻撃 : 勇者4に26のダメージ
勇者4のHP : 88

勇者1 , HP : 100 勇者2 , HP : 100 勇者3 , HP : 43 勇者4 , HP : 88
スライム1 , HP : 180 スライム2 , HP : 0 スライム3 , HP : 80

勇者3の攻撃 : スライム3に35のダメージ
スライム3のHP : 45
勇者1の攻撃 : スライム3に36のダメージ
スライム3のHP : 9
スライム3の攻撃 : 勇者2に31のダメージ
勇者2のHP : 69
スライム1の攻撃 : 勇者3に28のダメージ
勇者3のHP : 15
勇者2の攻撃 : スライム3に34のダメージ
スライム3のHP : 0
スライム3は力尽きた!!!
勇者4の攻撃 : スライム1に43のダメージ
スライム1のHP : 137

勇者1 , HP : 100 勇者2 , HP : 69 勇者3 , HP : 15 勇者4 , HP : 88
スライム1 , HP : 137 スライム2 , HP : 0 スライム3 , HP : 0

勇者1の攻撃 : スライム1に33のダメージ
スライム1のHP : 104
勇者4の攻撃 : スライム1に41のダメージ
スライム1のHP : 63
勇者2の攻撃 : スライム1に34のダメージ
スライム1のHP : 29
勇者3の攻撃 : スライム1に36のダメージ
スライム1のHP : 0
スライム1は力尽きた!!!

勇者チームチームの勝ち
戦闘終了

見事に勇者チーム勝利wwwwwwww

お互いに殴り合うだけの
単純なシミュレーションです。

ドラクエ2風になったんじゃないかと思います。

そう考えると
ドラクエ3以降は複雑怪奇ですねーー

すぐ作れる気がしないので
この先どうなることやら・・・

今回はここまでです。
それでは。

プログラムの文法は一通り学んだんだけど
どうやってプログラム書けばいいか分からないと言う方
朗報です。

ドラクエのシミュレーターを通じて
プログラムの作り方を学んでいきましょう。

解説動画はこちら







さてまず考えることは
小さなところからコツコツとです。

まずは1行から
慣れてきたら複数行
有る程度できるようになったら数千行の奴へ挑戦

と言うようにステップを踏んで
プログラムに慣れていきましょう。

最初はコピペからで十分です。
有る程度コードが打てるようになったら
次の段階へ進みましょう。

プログラムを考える時のコツですが

プログラムに基本は
1.入力 , 2.計算 , 3.出力 を考える
です。

1.入力させるデータ(構造) が何で
2.何をどう計算させるか(アルゴリズム)がどうなってて 
3.最終的に何を出すか(結果)

と言うことを考えます。

これは1行1行にも当てはまります。
全ての行で入力 , 計算 , 出力が行われているはずです。

一体何をどうしているのかを
把握しながらコードを書くのが
プログラムを書くコツになります。

ドラクエのシミュレーションを考えてみましょう。

想定されるシーンは
ドラクエ1

勇者 vs スライム
1対1の対決です。

ここではキャラクターとしては2体。
ステータスは共通のものを使うとします。

次に何をするかと言うと

お互いに攻撃しあってHPを減らしていき
HPが0になったら戦闘は終了しますので
それまで戦いを続けます。

最終的にはどちらかが勝つことになり
これが結果となります。

さて入力データを考えていきましょう。

2体のキャラクターですが
プログラムの入力データとしては
1つのクラスにまとめることができます。

次のようなクラスでまとめられます。

class character():

    def __init__(self,name,hp,attack,defence,agility):
        self.name = name
        self.hp = hp
        self.attack = attack
        self.defence = defence
        self.agility = agility

これを呼び出してみると
c1 = character('勇者',150,60,40,60)
c2 = character('スライム',100,50,30,56)

for k,v in c1.__dict__.items():
    print(k,v)
    
for k,v in c2.__dict__.items():
    print(k,v)
name 勇者
hp 150
attack 60
defence 40
agility 60
name スライム
hp 100
attack 50
defence 30
agility 56

こんな感じで勇者とスライム君ができました。

次はアルゴリズムを考えていきます。

アルゴリズム
1.ダメージ計算

攻撃力÷2-守備力÷4=ダメージ基礎値
ランダム値(ダメージ基礎値 ÷16+1)
ダメージ = ダメージ基礎値 プラスマイナス ランダム値

2.先頭順序
素早さ乱数の多い方が先に行動する
素早さ乱数 = 素早さ プラスマイナス10%のランダム値
例:400 = 360から440の間のランダム値

3.戦闘シーン
ダメージを算出してお互いのHPを削って
どちらかが0になったら終了


まずはダメージ計算です。
基礎値を算出してプラマイランダム値と言うのが
公式的な算出方法なようです。

シリーズによって多少の違いはありますが
基本的な考え方は一緒なのでこれを踏襲します。
import numpy as np

# ダメージ計算
def damage_calc(c1,c2):
    base = c1.attack //2 - c2.defence//4
    rand = base//16+1
    tmp = np.random.randint(0,rand*2+1)-rand
    damage = base + tmp
    return damage

次に先頭順序の計算です。

これを考えるには素早さ乱数がわからないとダメなので
先にこれを計算しておきます。
# 素早さ計算
def agility_calc(c):
    base = c.agility
    tmp = base//10
    result = np.random.randint(base-tmp,base+tmp+1)
    return result

# 先頭順序の計算
def turn_calc(c1,c2):
    a1 = agility_calc(c1)
    a2 = agility_calc(c2)
    if a1>=a2:
        return True
    else:
        return False

最後に先頭シーンを考えます。
お互いのHPを削って行って
HPが0になったら先頭終了です。

ターン中の計算と
先頭シーンの制御の部分で分けてみます。
# ターン中の計算
def turn(c1,c2):
    damage = damage_calc(c1,c2)
    c2.hp = c2.hp - damage if c2.hp - damage>=0 else 0
    print('{0}の攻撃 : {1}に{2}のダメージ'.format(c1.name,c2.name,damage))
    print('{0}のHP : {1}'.format(c2.name,c2.hp))
    if c2.hp<=0:
        print('{0}は力尽きた'.format(c2.name))
        return (c1,c2,0)
    damage = damage_calc(c2,c1)
    c1.hp = c1.hp - damage if c1.hp - damage>=0 else 0
    print('{0}の攻撃 : {1}に{2}のダメージ'.format(c2.name,c1.name,damage))
    print('{0}のHP : {1}'.format(c1.name,c1.hp))
    if c1.hp<=0:
        print('{0}は力尽きた'.format(c1.name))
        return (c1,c2,1)
    return (c1,c2,9)

# 戦闘
def battle(c1,c2):
    while True:
        # 勇者が先のターン
        if turn_calc(c1,c2):        
            c1,c2,flg = turn(c1,c2)
            if flg==0:
                print('終了 : {0}の勝ち'.format(c1.name))
                break
            elif flg==1:
                print('終了 : {0}の勝ち'.format(c2.name))
                break
        # モンスターが先のターン
        else:
            c2,c1,flg = turn(c2,c1)
            if flg==0:
                print('終了 : {0}の勝ち'.format(c2.name))
                break
            elif flg==1:
                print('終了 : {0}の勝ち'.format(c1.name))
                break


これで出来ました。

さてシミュレーションしてみましょう。
c1 = character('勇者',100,80,40,66)
c2 = character('スライム',180,80,30,66)

# 戦闘開始
battle(c1,c2)
勇者の攻撃 : スライムに33のダメージ
スライムのHP : 147
スライムの攻撃 : 勇者に31のダメージ
勇者のHP : 69
勇者の攻撃 : スライムに35のダメージ
スライムのHP : 112
スライムの攻撃 : 勇者に29のダメージ
勇者のHP : 40
スライムの攻撃 : 勇者に31のダメージ
勇者のHP : 9
勇者の攻撃 : スライムに34のダメージ
スライムのHP : 78
勇者の攻撃 : スライムに33のダメージ
スライムのHP : 45
スライムの攻撃 : 勇者に28のダメージ
勇者のHP : 0
勇者は力尽きた
終了 : スライムの勝ち

スライムが強すぎて勝てる気がしませんね。

アプリケーションは小さなプログラムの集合体です。
小さなプログラムは数行のコードだったりします。

何が入力で、何を計算させれば良いのかを
少しづつ考えていけば、次第に大きなものを
作れるようになってくと思います。

ドラクエのシミュレーションは
プログラムの初心者がプログラムの作り方を学ぶ
題材としてはうってつけだと思います。

まずは最低限の機能を実装していき
次第に大きくしていきます。

昔作った
ドラクエモンスターライトの
シミュレーターは複数 vs 複数のバトルなので
心折れそうでしたがアンドロイドアプリにすることができました。
(今は公開していませんが)

目標を決めるとそれに向かって努力するので
小さめの目標から初めて
達成を繰り返していくと言う過程を経ることで
大きな目標達成に繋げることができるのではないかと思います。

今回はここまでです。
それでは。





このページのトップヘ