Stage 7 入力を変換する
7-1 不要な文字を消去する
1-1 で決めたように、このゲームでは駒を動かすのに棋譜を使います。ですから棋譜が読めるようにならなければいけません。読めますよね?
ここでは棋譜の書き方なんてまともにやりませんから、しっかり勉強してきてください。
~~~~~~~~~~~~~~~
棋譜には、駒の動きを表すなくてはならない部分と、あってもなくても駒の動きがわかる部分とがあります。Q a x e6 ++ ! という場合を考えてみましょう。一番最後の "!" は記録している人が
これは妙手じゃ
と、うなった手であることを表しています。駒の動きには一切関係ありませんから、文字列からは削除します。
不要なのはこれだけではありません。Q a x e6 ++ ! の文字列をよくみてください。なんならプログラムみたいに配列に格納するところも想像してみてください。
いや、もういらないもんねーよ。
空白文字が格納されますよね。ぶっちゃけいらないでしょ。ですから空白も消します。
~~~~~~~~~~~~~~~
それではコーディングに入りましょう。board.py の s_analyze メソッドです。
def s_analyze(self, logger=None):
### LOGGER SETTING
logger = logger or self.logger
### ADJUSTING s
# removing spaces, !, ?
self.s = self.s.replace(' ', '').replace('!', '').replace('?', '')
# avoiding bugs
if len(self.s) == 0:
logger.debug('len(s) == 0')
return False
# the pattern of the normal format
match = re.match(r'^[PRNBQK]?[a-h]?[1-8]?[x]?[a-h][1-8](=[RNBQ]|e.p.)?[\++#]?$', self.s)
### NORMAL FORMAT
if match:
line = match.group() # line is the record matched into format
logger.info('line = {}'.format(line))
# what piece is moving
if line[0] in ['P', 'R', 'N', 'B', 'Q', 'K']:
piece = IO.ToggleType(line[0])
# deleting the info of piece because we do not use it any more
line = line.lstrip(line[0])
else:
# if not written the piece, it is PAWN's motion
piece = PAWN
logger.info('PIECE == {}'.format(piece))
# written info of what rank the piece comes from; frRANK starts from 0, but written No. is from 1
if line[0].isdecimal():
frFILE = OVERSIZE
frRANK = IO.ToggleType(line[0]) - 1
# deleting the number so that the sentence seems simpler
line = line.lstrip(line[0])
# written info of what file the piece comes from; frFILE starts from 0, but written No. is from 1
elif ord('a') <= ord(line[0]) <= ord('h') and ord('a') <= ord(line[1]) <= ord('x'):
frFILE = IO.ToggleType(line[0]) - 1
frRANK = OVERSIZE
# deleting only the first character of line
line = line[1:]
# nothing is written about where the piece comes from
else:
frFILE = OVERSIZE
frRANK = OVERSIZE
logger.info('FR = {}, {}'.format(frFILE, frRANK))
# whether the piece has captured one of the opponent's pieces
if line[0] == 'x':
CAPTURED = True
line = line.lstrip(line[0])
else:
CAPTURED = False
# where the piece goes to (certainly written); toFILE and toRANK starts from 0
toFILE = IO.ToggleType(line[0]) - 1
toRANK = IO.ToggleType(line[1]) - 1
logger.info('TO = {}, {}'.format(toFILE, toRANK))
# promotion
if '=' in line:
promote = IO.ToggleType(line[line.index('=') + 1])
else:
promote = EMPTY
logger.info('promote = {}'.format(promote))
# counting up all the available candidates searching all the squares
candidates = []
for fil in range(SIZE):
# when frFILE is written
if fundam.InSize(frFILE) and frFILE != fil:
continue
for ran in range(SIZE):
# when frRANK is written
if fundam.InSize(frRANK) and frRANK != ran:
continue
# if the piece is not own, you cannot move it
if self.board[fil][ran] != self.player * piece:
continue
# in case of unavailable motion
if self.motionjudge(fil, ran, toFILE, toRANK, promote) == False:
continue
candidates.append([fil, ran])
logger.info('candidates = {}'.format(candidates))
# checking all the candidates
for reference in range(len(candidates)):
# copying the board
local_board = Board(board=self.board, target=self.ep_target, castl_k=self.castl_k, castl_q=self.castl_q, player=self.player, turn=self.turn, s=self.s)
# moving the candidate
local_board.move(candidates[reference][FILE], candidates[reference][RANK], toFILE, toRANK, promote)
# capture; searching for the opponent's piece that has disappeared
if CAPTURED or 'e.p.' in line:
# normal capturing; TO is opponent's
if fundam.PosNeg(self.board[toFILE][toRANK]) == -self.player:
pass
# en passan to Q-side
elif fundam.InSize(toRANK - 1) and fundam.PosNeg(self.board[toFILE][toRANK - 1]) == -self.player and fundam.PosNeg(local_board.board[toFILE][toRANK - 1]) == EMPTY:
pass
# en passan to K-side
elif fundam.InSize(toRANK + 1) and fundam.PosNeg(self.board[toFILE][toRANK + 1]) == -self.player and fundam.PosNeg(local_board.board[toFILE][toRANK + 1]) == EMPTY:
pass
# here no piece can capture a piece
else:
logger.info('{} does not capture any piece'.format(candidates[reference]))
del candidates[reference]
reference -= 1 # at loop's head, reference increases
continue
# check
if line.count('+') > local_board.checkcounter(-self.player):
logger.info('{} is short of the number of check'.format(candidates[reference]))
del candidates[reference]
reference -= 1 # at loop's head, reference increases
continue
# checkmate
if '#' in line and local_board.checkmatejudge(-self.player) == False:
logger.info('{} does not checkmate'.format(candidates[reference]))
del candidates[reference]
reference -= 1 # at loop's head, reference increases
continue
# en passant
if 'e.p.' in line and self.board[toFILE][toRANK] != EMPTY:
logger.info('{} does not en passant'.format(candidates[reference]))
del candidates[reference]
reference -= 1 # at loop's head, reference increases
continue
# normal return
if len(candidates) == 1:
logger.info('NORMALLY RETURNED')
return [candidates[0][FILE], candidates[0][RANK], toFILE, toRANK, promote]
# when another candidate is available
elif len(candidates) > 1:
logger.warning('THERE IS ANOTHER MOVE')
return [candidates[0][FILE], candidates[0][RANK], toFILE, toRANK, promote]
# no candidates are available
else:
logger.info('THERE IS NO MOVE')
return False
# NOT IN NORMAL FORMAT
else:
# game set; take note that player themselves cannot win by inputting these codes
if self.s == '1/2-1/2':
logger.info('DRAW GAME')
return EMPTY
elif self.s == '1-0' and self.player == BLACK:
logger.info('WHITE WINS')
return WHITE
elif self.s == '0-1' and self.player == WHITE:
logger.info('BLACK WINS')
return BLACK
# check whether s represents castling
# rank setting
if self.player == WHITE:
rank = 1 - 1
elif self.player == BLACK:
rank = 8 - 1
else:
logger.error('UNEXPECTED PLAYER VALUE in s_analyze')
print('SYSTEM ERROR')
sys.exit('SYSTEM ERROR')
# Q-side
if self.s in ['O-O-O', 'o-o-o', '0-0-0'] and self.board[e - 1][rank] == self.player * KING:
logger.info('format is {}, castl is {}'.format(self.s, self.castl_q))
return [e - 1, rank, c - 1, rank, EMPTY]
# K-side
elif self.s in ['O-O', 'o-o', '0-0'] and self.board[e - 1][rank] == self.player * KING:
logger.info('format is {}, castl is {}'.format(self.s, self.castl_k))
return [e - 1, rank, g - 1, rank, EMPTY]
# invalid format
else:
logger.debug('INVALID FORMAT')
return False
なっっっが...
ちょっと脅しただけですって。これを全 9 回に分けてご説明します。今回使うのはここだけです。一瞬でしょ?
def s_analyze(self, logger=None):
### LOGGER SETTING
logger = logger or self.logger
### ADJUSTING s
# removing spaces
self.s = self.s.replace(' ', '').replace('!', '').replace('?', '')
# avoiding bugs
if len(self.s) == 0:
logger.debug('len(s) == 0')
return False
~~~~~~~~~~~~~~~
いつも通りロガーを設定したら、空白 " ", エクスクラメーション "!", クエスチョン "?" を全て空文字 "" に置き換えます。
def s_analyze(self, logger=None):
### LOGGER SETTING
logger = logger or self.logger
### ADJUSTING s
# removing spaces
self.s = self.s.replace(' ', '').replace('!', '').replace('?', '')
replace メソッドを使っていますね。
なお、str 型はイミュータブル(書き換え不可能)ですから、del self.s[-1] のようなリスト操作はできません。まあこれくらいは常識ですね。
いみゅーたぶる...?
いや、リンク貼ってんだからそこで勉強せえよ。
次の if 文はバグを回避するために入れたものです。
# avoiding bugs
if len(self.s) == 0:
logger.debug('len(s) == 0')
return False
後で s の最後尾の文字を調べるんですが、文字列を全部消してしまって、仮に s に文字が入っていない状態 (len(s) == 0) になると、「インデックス無効」とエラーを吐き出してシステムが強制終了されてしまいますから。