今回の仕様というか概要というか、まぁそのあたりは以下。前回の主な差分にあたる、利き関係のみ。
前回記事からの大きな違いは「利き」の実現方法が違うってことです。今回の方法では利きをベクトルの集合で表現しているので、ある方向に対しての走査が可能になりました。これによってある駒の動けるマスを敵味方の駒や盤の端を考慮して算出しなければならないケースも容易に対応できるようになったのではないかと思います。
あともう1つ、駒の名前をen/ja表記で返すメソッドを追加しています。歩なら"Fu"/"歩"です。まぁこの辺はあって損はないだろ、ってことで追加しました。ただ、__str__()でこの機能を実装して、getName()の方では正式名称の方を返す仕様にしておいても良かったかもしれません。以下ソース。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class vec:
"""
基底ベクトル+長さで移動可能な座標を表現する。
筋、段の定義において、数の小さい側をマイナス方向とする。
"""
SHORT=1
LONG=9
RIGHT=-1
LEFT=1
UPPER=-1
DOWN=1
def __init__(self,base,length=SHORT):
if(not type(base)==tuple): raise Exception, "Vector.__init__: type(base)"
if(not len(base)==2): raise Exception, "Vector: __init__(), len(base)"
if(base[0]==0 and base[1]==0): raise Exception, "Vector: __init__()"
self.direction = base
self.length = length
def __str__(self, vector=True):
if(vector):
r=self.direction
return "("+self.direction.__str__()+", "+(lambda x: "SHORT" if x==vec.SHORT else "LONG")(self.length)+")"
else:
pass
def eqDirection(self, suji, dan):
if(self.direction[0]==suji and self.direction[1]==dan):
return True
return False
@classmethod
def UpperLeft(cls, length=SHORT):
return vec((vec.LEFT,vec.UPPER),length)
@classmethod
def Upper(cls,length=SHORT):
return vec((0,vec.UPPER),length)
@classmethod
def UpperRight(cls,length=SHORT):
return vec((vec.RIGHT,vec.UPPER),length)
@classmethod
def Left(cls, length=SHORT):
return vec((vec.LEFT,0),length)
@classmethod
def Right(cls, length=SHORT):
return vec((vec.RIGHT,0),length)
@classmethod
def DownLeft(cls, length=SHORT):
return vec((vec.LEFT,vec.DOWN),length)
@classmethod
def Down(cls, length=SHORT):
return vec((0,vec.DOWN), length)
@classmethod
def DownRight(cls, length=SHORT):
return vec((vec.RIGHT,vec.DOWN), length)
@classmethod
def Ke_Right(cls, length=SHORT):
return vec((vec.RIGHT,vec.UPPER*2), vec.SHORT)
@classmethod
def Ke_Left(cls, length=SHORT):
return vec((vec.LEFT, vec.UPPER*2), vec.SHORT)
def __iter__(self):
for l in range(1,self.length):
yield tuple(map(lambda x: x*l, self.direction))
class Piece:
def getName(self, en=True):
return self.__class__.__name__
def getEffects(self):
raise MyException, "abstract method"
def getPromote(self):
raise MyException, "Not Implements yet"
def getUnpromote(self):
raise PromoteException, "cannot unpromote "+self.getName()
def isPromote(self):
return False
class Fu(Piece):
def getName(self,en=True):
return "Fu" if en else "歩"
def getEffects(self):
return [vec.Upper()]
def getPromote(self):
return To()
class Ky(Piece):
def getName(self,en=True):
return "Ky" if en else "香"
def getEffects(self):
return [vec.Upper(length=vec.LONG)]
def getPromote(self):
return Ny()
class Ke(Piece):
def getName(self,en=True):
return "Ke" if en else "桂"
def getEffects(self):
return [vec.Ke_Right(), vec.Ke_Left()]
def getPromote(self):
return Nk()
class Gi(Piece):
def getName(self,en=True):
return "Gi" if en else "銀"
def getEffects(self):
return [vec.UpperRight(),vec.Upper(),vec.UpperLeft(),
vec.DownRight(),vec.DownLeft()]
def getPromote(self):
return Ng()
class Ki(Piece):
def getName(self,en=True):
return "Ki" if en else "金"
def getEffects(self):
return [vec.UpperRight(),vec.Upper(),vec.UpperLeft(),
vec.Right(),vec.Left(),
vec.Down()]
def getPromote(self):
raise PromoteException, "Ki cannot promote"
class Ka(Piece):
def getName(self,en=True):
return "Ka" if en else "角"
def getEffects(self):
return [vec.UpperRight(length=vec.LONG),
vec.UpperLeft(length=vec.LONG),
vec.DownRight(length=vec.LONG),
vec.DownLeft(length=vec.LONG)]
def getPromote(self):
return Um()
class Hi(Piece):
def getName(self,en=True):
return "Hi" if en else "飛"
def getEffects(self):
return [vec.Upper(length=vec.LONG),
vec.Left(length=vec.LONG),vec.Right(length=vec.LONG),
vec.Down(length=vec.LONG)]
def getPromote(self):
return Ry()
class Ou(Piece):
def getName(self,en=True):
return "Ou" if en else "玉"
def getEffects(self):
return [vec.UpperRight(),vec.Upper(),vec.UpperLeft(),
vec.Right(),vec.Left(),
vec.DownRight(),vec.Down(),vec.DownLeft()]
def getPromote(self):
raise PromoteException, "Ou cannot promote"
class PromotedPiece(Piece):
def isPromote(self): return True
def getPromote(self):
raise PromoteException, "Already promoted"
def getUnpromote(self):
raise MyException,"Not implements yet"
class To(PromotedPiece):
def getName(self,en=True):
return "To" if en else "と"
def getEffects(self):
return Ki().getEffects()
def getUnpromote(self):
return Fu()
class Ny(PromotedPiece):
def getName(self,en=True):
return "Ny" if en else "杏"
def getEffects(self):
return Ki().getEffects()
def getUnpromote(self):
return Ky()
class Nk(PromotedPiece):
def getName(self,en=True):
return "Nk" if en else "圭"
def getEffects(self):
return Ki().getEffects()
def getUnpromote(self):
return Ke()
class Ng(PromotedPiece):
def getName(self,en=True):
return "Ng" if en else "全"
def getEffects(self):
return Ki().getEffects()
def getUnpromote(self):
return Gi()
class Um(PromotedPiece):
def getName(self,en=True):
return "Um" if en else "馬"
def __init__(self):
self.org = Ka()
def getEffects(self):
return [vec.Upper(),vec.Down(),vec.Right(),vec.Left()]+self.org.getEffects()
def getUnpromote(self):
return self.org
class Ry(PromotedPiece):
def getName(self,en=True):
return "Ry" if en else "龍"
def __init__(self):
self.org = Hi()
def getEffects(self):
return [vec.UpperRight(),vec.UpperLeft(),vec.DownRight(),vec.DownLeft()]+self.org.getEffects()
def getUnpromote(self):
return self.org
class MyException(Exception):
"""
実装してない場合に投げる例外とか割とデバッグ用途のexceptionクラス
"""
pass
class PromoteException(MyException):
pass
##############################################
# 衝突判定専用で定義した関数
# OO的にはあんまりよくないので今回の実装のテストコード専用といった位置づけ
def tpladd(t1,t2):
"""
長さ=2のタプルを要素同士足し算
"""
return (t1[0]+t2[0], t1[1]+t2[1])
def isOnBoard(tpl):
"""
(筋, 段)のタプルを引数にとる
"""
if( (1<=tpl[0] and tpl[0]<=9) and (1<=tpl[1] and tpl[1]<=9) ): return True
return False
##############################################
def test():
tests = [Fu(), Ky(), Ke(), Gi(), Ki(), Ka(), Hi(), Ou(),
To(), Ny(), Nk(), Ng(), Um(), Ry()]
print "\n*************************************"
print "[TEST] getName"
for p in tests:
print p.getName() + " : "+p.getName(en=False)
print "\n*************************************"
print "[TEST] getPromote"
for p in tests:
try:
print p.getName()+" => "+p.getPromote().getName() + " : "+p.getPromote().getName(en=False)
except Exception, e:
print "**("+p.getName()+")"+e.__str__()
print "\n*************************************"
print "[TEST] getUnpromote"
for p in tests:
try:
print p.getName()+" => "+p.getUnpromote().getName()+" : "+p.getUnpromote().getName(en=False)
except Exception, e:
print "**("+p.getName()+")"+e.__str__()
print "\n*************************************"
print "[TEST] getEffects"
print " ある1方向への利きを\n\t((筋,段), ベクトルの長さ)\nの形式で表示。"
print " ※あくまで__str__()で文字列化した時の表現方法。内部表現も限りなく似ているけど。"
for p in tests:
try:
print "\n"+p.getName(en=False)+":"
for pe in p.getEffects():
print "\t"+pe.__str__()
except Exception,e:
print "**("+p.getName()+")"+e.__str__()
print "\n*************************************"
print "[TEST] getPromote->getEffects"
for p in tests:
try:
print "\n"+p.getPromote().getName(en=False)+":"
for pe in p.getPromote().getEffects():
print "\t"+pe.__str__()
except Exception,e:
print "**("+p.getName()+")"+e.__str__()
print "\n*************************************"
print "[TEST] getUnpromote->getEffects"
for p in tests:
try:
print "\n"+p.getUnpromote().getName(en=False)+":"
for pe in p.getUnpromote().getEffects():
print "\t"+pe.__str__()
except Exception,e:
print "**("+p.getName()+")"+e.__str__()
print "\n*************************************"
print "[TEST] 龍の上方向への利きをforで走査してみる"
print " 現在この龍は3四にいるとする。"
print " 利きを管理するリストから\"上\"を明示して指定する方法がない点は要改善か?"
pos = (3,4) # 龍が3四にいるとする
for eff in Ry().getEffects():
if(eff.eqDirection(0,vec.UPPER)):
for ef in eff:
"""
vecをイテレータプロトコルに対応させているため、
利きのある1方向に対してはfor文で順番に見ていくことが可能になっている。
forで回せるようになったことで、味方or敵の駒とか、盤外との利きの衝突判定を行うのが楽になる。
"""
moved = tpladd(pos, ef)
if(not isOnBoard(moved)):
print "*"+ef.__str__()+" --- out of board"
else:
print ef
if(__name__=="__main__"):
test()
今回実装の小ネタとして挙げたいのは
です。まぁ"python イテレータプロトコル"とか、"python __iter__"でググればわかりやすい解説ページが出ますのでここで特に解説はしないことにします。要は、pythonのfor文はinが使えて何かと便利なのですが、自前で作ったクラスでもそういうforの書き方をしてみたい時にイテレータプロトコルの出番だよ、って話です。
yieldについてはC#でも同様の演算子が存在します。ちょっとわかりにくいですがpythonの方で分からなかったらそっちで調べるのもアリかと。まぁ、yieldを使わずにやる(next()とStopIterationで実装する)方法もありますし、yieldよりはそっちのが理解しやすい気はします。
test()の実行結果は以下。
*************************************
[TEST] getName
Fu : 歩
Ky : 香
Ke : 桂
Gi : 銀
Ki : 金
Ka : 角
Hi : 飛
Ou : 玉
To : と
Ny : 杏
Nk : 圭
Ng : 全
Um : 馬
Ry : 龍
*************************************
[TEST] getPromote
Fu => To : と
Ky => Ny : 杏
Ke => Nk : 圭
Gi => Ng : 全
**(Ki)Ki cannot promote
Ka => Um : 馬
Hi => Ry : 龍
**(Ou)Ou cannot promote
**(To)Already promoted
**(Ny)Already promoted
**(Nk)Already promoted
**(Ng)Already promoted
**(Um)Already promoted
**(Ry)Already promoted
*************************************
[TEST] getUnpromote
**(Fu)cannot unpromote Fu
**(Ky)cannot unpromote Ky
**(Ke)cannot unpromote Ke
**(Gi)cannot unpromote Gi
**(Ki)cannot unpromote Ki
**(Ka)cannot unpromote Ka
**(Hi)cannot unpromote Hi
**(Ou)cannot unpromote Ou
To => Fu : 歩
Ny => Ky : 香
Nk => Ke : 桂
Ng => Gi : 銀
Um => Ka : 角
Ry => Hi : 飛
*************************************
[TEST] getEffects
ある1方向への利きを
((筋,段), ベクトルの長さ)
の形式で表示。
※あくまで__str__()で文字列化した時の表現方法。内部表現も限りなく似ているけど。
歩:
((0, -1), SHORT)
香:
((0, -1), LONG)
桂:
((-1, -2), SHORT)
((1, -2), SHORT)
銀:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 1), SHORT)
((1, 1), SHORT)
金:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((0, 1), SHORT)
角:
((-1, -1), LONG)
((1, -1), LONG)
((-1, 1), LONG)
((1, 1), LONG)
飛:
((0, -1), LONG)
((1, 0), LONG)
((-1, 0), LONG)
((0, 1), LONG)
玉:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((-1, 1), SHORT)
((0, 1), SHORT)
((1, 1), SHORT)
と:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((0, 1), SHORT)
杏:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((0, 1), SHORT)
圭:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((0, 1), SHORT)
全:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((0, 1), SHORT)
馬:
((0, -1), SHORT)
((0, 1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((-1, -1), LONG)
((1, -1), LONG)
((-1, 1), LONG)
((1, 1), LONG)
龍:
((-1, -1), SHORT)
((1, -1), SHORT)
((-1, 1), SHORT)
((1, 1), SHORT)
((0, -1), LONG)
((1, 0), LONG)
((-1, 0), LONG)
((0, 1), LONG)
*************************************
[TEST] getPromote->getEffects
と:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((0, 1), SHORT)
杏:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((0, 1), SHORT)
圭:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((0, 1), SHORT)
全:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((0, 1), SHORT)
**(Ki)Ki cannot promote
馬:
((0, -1), SHORT)
((0, 1), SHORT)
((-1, 0), SHORT)
((1, 0), SHORT)
((-1, -1), LONG)
((1, -1), LONG)
((-1, 1), LONG)
((1, 1), LONG)
龍:
((-1, -1), SHORT)
((1, -1), SHORT)
((-1, 1), SHORT)
((1, 1), SHORT)
((0, -1), LONG)
((1, 0), LONG)
((-1, 0), LONG)
((0, 1), LONG)
**(Ou)Ou cannot promote
**(To)Already promoted
**(Ny)Already promoted
**(Nk)Already promoted
**(Ng)Already promoted
**(Um)Already promoted
**(Ry)Already promoted
*************************************
[TEST] getUnpromote->getEffects
**(Fu)cannot unpromote Fu
**(Ky)cannot unpromote Ky
**(Ke)cannot unpromote Ke
**(Gi)cannot unpromote Gi
**(Ki)cannot unpromote Ki
**(Ka)cannot unpromote Ka
**(Hi)cannot unpromote Hi
**(Ou)cannot unpromote Ou
歩:
((0, -1), SHORT)
香:
((0, -1), LONG)
桂:
((-1, -2), SHORT)
((1, -2), SHORT)
銀:
((-1, -1), SHORT)
((0, -1), SHORT)
((1, -1), SHORT)
((-1, 1), SHORT)
((1, 1), SHORT)
角:
((-1, -1), LONG)
((1, -1), LONG)
((-1, 1), LONG)
((1, 1), LONG)
飛:
((0, -1), LONG)
((1, 0), LONG)
((-1, 0), LONG)
((0, 1), LONG)
*************************************
[TEST] 龍の上方向への利きをforで走査してみる
現在この龍は3四にいるとする。
利きを管理するリストから"上"を明示して指定する方法がない点は要改善か?
(0, -1)
(0, -2)
(0, -3)
*(0, -4) --- out of board
*(0, -5) --- out of board
*(0, -6) --- out of board
*(0, -7) --- out of board
*(0, -8) --- out of board
まあ概ねうまくいってるのではないかと思います...あまり自信はないですが。何が自信ないかって、テストコードが一番自信ないです。ろくにテストはやったことないし、JUnitとかもほぼ知らないし。ちゃんと網羅できてるのか、冗長な部分はないか、とかが気がかりです。pythonにもJUnitと似た使い勝手のテストフレームワークがあった気がしますが今回それは使ってません。そろそろVimから本格的にEclipseにシフトした方がいいかなあ?