オブジェクト指向で戦闘シミュレーション?(4)
前回は「艦これ」の戦闘のうち、砲撃戦の攻撃のみ計算させてみました。
今度は雷撃戦の攻撃も計算させてみます。
砲撃戦と雷撃戦の攻撃力計算の差
前回のプログラムのうち、PlayerクラスのGetOffenseメソッドで攻撃力計算をさせていました。
これをコピーして、与える値や計算の違うところを修正すれば、それでもプログラムは完成します。
しかし、大まかな計算の流れ()は同じなので、違う部分だけを書き直して、共通部分は再利用したほうがプログラムの見通しが良くなったり、デバッグが減ったりと利点があります。
今回はこれを、オブジェクト指向の「継承」を使って実現してみたいと思います。
砲撃戦と雷撃戦の計算のうち、変更が加わるのは下記になります。
・陣形補正
・損傷補正
・基本攻撃力計算式
・攻撃力キャップ
・クリティカル発生率
プログラム書き換え
攻撃力計算はPlayerクラスで行っていますが、今回は1人のPlayerで攻撃種類を切り替えたいので、継承で派生させるための(攻撃力計算用)Offense〜クラスを新たに作りたいと思います。
まずはもととなる、砲撃戦計算OffenseBombクラス。
class OffenseBomb():
#昼・砲撃戦バージョン
def __init__(self, player):
self.Player= player
def JinkeiHosei(self, Jinkei):
#Jinkei 0:単縦/1:複縦/2:輪/3:梯/4:単横/5:警戒
return [1.00, 0.80, 0.70, 0.75, 0.60, 0.5][Jinkei]
def KousenKeitaiHosei(self, KousenKeitai):
#KousenKeitai 0:同航戦/1:反航戦/2:T字有利/3:T字不利
return [1.0, 0.8, 1.2, 0.6][KousenKeitai]
def SonsyoHosei(self, Sonsyo):
#Sonsyo 0:全快/1:小破/2:中破/3:大破/4:攻撃不能
return [1.0, 1.0, 0.7, 0.4, 0.0][Sonsyo]
def KihonKougeki(self, Player, KaisyuHou):
#基本攻撃力
return (Player.Karyoku +KaisyuHou +5)
def Cap(self):
#キャップ:昼戦砲撃戦
return 180.0
def ProbChritical(self, HitLev):
#クリティカル発生率
return (0.125 *math.sqrt(HitLev) +0.01)
def Get(self, Player, Jinkei= 0, KousenKeitai= 0, KaisyuHou= 0.0):
#各種パラメータ
Sonsyo=0 #0:全快/1:小破/2:中破/3:大破/4:攻撃不能
Chritical= 1.0 #クリティカル補正
CapMaeHosei= self.KousenKeitaiHosei(KousenKeitai) \
*self.JinkeiHosei(Jinkei) \
*self.SonsyoHosei(Sonsyo)
#命中・クリティカル判定
HitLev= (self.Player.GetHit() -self.Player.GetAvoidance()) /100.0
ChriticalDice= random.random()
if ChriticalDice>=HitLev: #MISS
return 0
if ChriticalDice<self.ProbChritical(HitLev): #クリティカルヒット発生
Chritical= 1.5
#キャップ前攻撃力
CapMae= self.KihonKougeki(Player, KaisyuHou) *CapMaeHosei
#攻撃力キャップ
if CapMae<=self.Cap():
CapGo= CapMae
else:
CapGo= self.Cap() + math.sqrt(CapMae -self.Cap())
#最終攻撃力算出
A= int(CapGo)
SaisyuKougeki= int(A *Chritical) # クリティカル熟練度補正
return SaisyuKougeki
class Player():
#-- 中略 --#
def GetOffense(self):
OffenseAction= OffenseBomb(self) #上記のクラスを使う
return OffenseAction.Get(self) #攻撃力を返す
クラスの継承
次に、OffenseBombクラスを継承して、雷撃戦計算OffenseLightクラスを作ってみます。
#OffenseBombの後方に、下記の継承するクラスを書く
class OffenseLight(OffenseBomb):
def JinkeiHosei(self, Jinkei):
#Jinkei 0:単縦/1:複縦/2:輪/3:梯/4:単横/5:警戒
return [1.0, 0.8, 0.7, 0.6, 0.6, 1.0][Jinkei]
def SonsyoHosei(self, Sonsyo):
#Sonsyo 0:全快/1:小破/2:中破/3:大破/4:攻撃不能
return [1.0, 1.0, 0.8, 0.0, 0.0][Sonsyo]
def KihonKougeki(self, Player, KaisyuRai):
#基本攻撃力
return (Player.Raisou +KaisyuRai +5)
def Cap(self):
#キャップ:昼戦砲撃戦以外
return 150.0
def ProbChritical(self, HitLev):
#クリティカル発生率
return (0.15 *math.sqrt(HitLev) +0.01)
class OffenseLight(OffenseBomb):と書くことで、OffenseBombクラスを継承することを指定します。
この中に継承元のメソッドと同名を書くことで、メソッドを上書きします。
上書きしなかったメソッドは、そのまま使うことができます。
なので、ここでの雷撃計算では、書き換えた5つのメソッドが書き換わり、OffenseBombクラスで用意したGetメソッドは同じように使うことができます。
意外と簡単に済んでしまいました。
ついでにポリモーフィズム
Playerクラスには、攻撃力を返すGetOffenseメソッドで、砲撃と雷撃を切り替えるプログラムを組みました。
class Player():
#-- 中略 --#
def GetOffense(self, action): #action= 雷撃:0、砲撃:1
if action==0:
OffenseAction= OffenseLight(self)
elif action==1:
OffenseAction= OffenseBomb(self)
return OffenseAction.Get(self)
#クラスが変わっても同名のメソッド(Get)を使うことができる
OffenseActionオブジェクトは、砲撃か雷撃かでクラス(OffenseBomb、OffenseLight)が変わります。
しかし、それらのクラスに同名のメソッドがあれば、それを使うことができます。(上記のreturnの所)
まあ当たり前なんじゃない?と思えたならば素晴らしい!
同じ名前のメソッドは同じように使えるように作れば、「return OffenseAction.Get(self)」はもう修正する必要がなくなります。
特に、継承を使うと、これが簡単にできることがオブジェクト指向の利点です。
昔からプログラムをしていると、違うメソッドを指しているのでエラーになるのではと直感で勘ぐってしまいますが、ポリモーフィズムならば大丈夫です、はい。
プログラムを試してみる
前回と同じように、paiza.ioにプログラムを用意しました。
エディタに用意されたプログラムは書き換えることができますし、ページを閉じれば元に戻りますので、自由に実行してみてください。