Stage 10 記録からゲームを進める
10-2 棋譜の内容を解読する
さて、構文解析の時間です。前回用意した棋譜の文字列 line から、駒を動かすのに役立つ情報だけをもれなくダブりなく解読していきます。
どうやるかって言いますと、for 文で 1 文字ずつ letter という str 型変数に line の文字を取り込んでいきます。
# DETECTING EACH LETTER IN LINE
for letter in line:
# when you come to the end of a sentence
if letter in [' ', '\t', '\n', ',', '.']:
とりあえずこの文字を local_board.s の中にブチ込んでって、空白や改行が割り込んだら打ち切り、local_board.s に残った文字列が s_analyze にうまいこと引っかかってくれるかを見ます。フローチャートで表すとこんな感じ。
~~~~~~~~~~~~~~~
例えば
line = "1\ta4 c5 \n2\tg3 "
となっていれば、まず最初に letter に '1' が代入され、これが local_board.s に移ります。
local_board.s == "1"
ついで letter には line[1] つまり '\t' が入りますが、こいつは空白の文字なので local_board.s はここまでで棋譜として扱えるか検証されます。当然 "1" では棋譜となりませんから無視してしまいます。
次は letter に line[2] の 'a' が入って local_board.s に移行されますね。
local_board.s == "a"
その次は line[3] の '4' が入ります。
local_board.s == "a4"
その次が空白文字なので、local_board.s はここで打ち切り。棋譜として扱えるか検証します。"a4" は立派に棋譜として使えますので、影武者の盤面 local_board で実際に a4 の動きをさせ、
SUBRECADDRESS に
記録を取ります。
~~~~~~~~~~~~~~~
該当箇所のコードはおよそこんな感じ。
# detectong each letter in line
for letter in line:
# when you come to the end of a sentence
if letter in [' ', '\t', '\n', ',', '.']:
logger.info('local_s is {}'.format(local_board.s))
motion = local_board.s_analyze()
# normal motion
if type(motion) is list:
...
# game set
elif type(motion) is int:
...
# initializing the local_s
local_board.s = ''
# the sentence does not end yet
else:
local_board.s = ''.join([local_board.s, letter])
logger.debug('local_s = {}'.format(local_board.s))
local_board.s を打ち切ったのが for 直下の if で条件分けしてるところ、local_board.s に文字入れてくのが else で除外してるところです。
# DETECTING EACH LETTER IN LINE
for letter in line:
# when you come to the end of a sentence
if letter in [' ', '\t', '\n', ',', '.']:
logger.info('local_board.s is {}'.format(local_board.s))
motion = local_board.s_analyze()
# normal motion
if type(motion) is list:
...
# game set
elif type(motion) is int:
...
# initializing local_board.s
local_board.s = ''
# the sentence does not end yet
else:
local_board.s = ''.join([local_board.s, letter])
logger.debug('local_board.s = {}'.format(local_board.s))
~~~~~~~~~~~~~~~
各分岐の中身を見ていきましょう。今回は先に else の方、「letter が空白文字でない場合」を確認します。
# the sentence does not end yet
else:
local_board.s = ''.join([local_board.s, letter])
logger.debug('local_s = {}'.format(local_board.s))
join メソッドを使って ''.join([local_board.s, letter])
としているのは、ただ単に local_board.s の後ろに letter の文字をくっつけてるだけ。これでおしまい。
for 直下の if で分けられた方、「letter が空白文字の場合」は
- local_board.s が出来上がってる
- どうあがいても棋譜としての体裁を成さない
のどちらかのはずですから、s_analyze を発動、motion に返り値を格納します。
# when you come to the end of a sentence
if letter in [' ', '\t', '\n', ',', '.']:
logger.info('local_s is {}'.format(local_board.s))
motion = local_board.s_analyze()
...
駒を動かすときは list, 勝敗がついたときは int がリターンされますから、それによって場合分けしています。
意味を成さないときは何もしないの?
あとで local_board.s が使える場合と合流しますので、そのときに。
~~~~~~~~~~~~~~~
まずは駒を動かす場合です。実際に move メソッドで駒を動かし、記録を
SUBRECADDRESS
にとります。
# normal motion
if type(motion) is list:
local_board.move(*motion)
local_board.record(SUBRECADDRESS) # all the local moves are recorded on the sub file
if local_board.player == BLACK:
local_board.turn += 1
local_board.player *= -1
パラメーターを変えるのを忘れないように。
# normal motion
if type(motion) is list:
local_board.move(*motion)
local_board.record(SUBRECADDRESS) # all the local moves are recorded on the sub file
if local_board.player == BLACK:
local_board.turn += 1
local_board.player *= -1
playmode でやったのとおんなじやないかい
8-6 でやりましたね。
~~~~~~~~~~~~~~~
引数で要請されたターン・プレーヤーにたどり着いていれば、リターン作業に入ります。
# reaching destination
if local_board.turn == destination_turn and local_board.player == destination_player:
logger.info('trace succeeded')
if isrecwrite:
# copying the file
f = open(MAINRECADDRESS, 'w')
g = open(SUBRECADDRESS, 'r')
f.write(g.read())
f.close()
g.close()
return local_board
isrecwrite が False つまり「MAINRECADDRESS を書き換えなくていい」場合はこのまま local_board をリターンします。
# reaching destination
if local_board.turn == destination_turn and local_board.player == destination_player:
logger.info('trace succeeded')
if isrecwrite:
# copying the file
f = open(MAINRECADDRESS, 'w')
g = open(SUBRECADDRESS, 'r')
f.write(g.read())
f.close()
g.close()
return local_board
一方 isrecwrite が True つまり「MAINRECADDRESS を書き換える」場合は SUBRECADDRESS の中身を MAINRECADDRESS に記録を転記しなければいけません。
サブをそのままメインに入れるのか
# reaching destination
if local_board.turn == destination_turn and local_board.player == destination_player:
logger.info('trace succeeded')
if isrecwrite:
# copying the file
f = open(MAINRECADDRESS, 'w')
g = open(SUBRECADDRESS, 'r')
f.write(g.read())
f.close()
g.close()
return local_board
そして MAINRECADDRESS を書き換えようが書き換えまいが、リターンするのは local_board です。
~~~~~~~~~~~~~~~
一方決着がついていれば駒を動かすことはしません(できません)し、SUBRECADDRESS に記録をとることもしません。isrecwrite で MAINRECADDRESS に転記が必要と判断されたら、サブの中身を転記します。
# game set
elif type(motion) is int:
print('GAME SET')
if isrecwrite:
# copying the record
f = open(MAINRECADDRESS, 'w')
g = open(SUBRECADDRESS, 'r')
f.write(g.read())
f.close()
g.close()
return motion
そして
motion をリターン
します。
return motion
つまり
返り値は勝ったプレーヤー番号、型は int
です。お間違えなきよう。
~~~~~~~~~~~~~~~
for 文を抜けた後にもう一度同じことをやります。リターンまで同じです。
# last one local_board.s; the same as in the for loop
logger.info('local_board.s is {}'.format(local_board.s))
motion = local_board.s_analyze()
if type(motion) is list:
local_board.move(*motion)
local_board.record(SUBRECADDRESS)
if local_board.player == BLACK:
local_board.turn += 1
local_board.player *= -1
# reaching destination
if local_board.turn == destination_turn and local_board.player == destination_player:
logger.info('trace succeeded')
if isrecwrite:
f = open(MAINRECADDRESS, 'w')
g = open(SUBRECADDRESS, 'r')
f.write(g.read())
f.close()
g.close()
return local_board
elif type(motion) is int:
if isrecwrite:
f = open(MAINRECADDRESS, 'w')
g = open(SUBRECADDRESS, 'r')
f.write(g.read())
f.close()
g.close()
return motion
やらんでええやろ
これをやらないと、棋譜の最後の文が認識されない恐れがあります。嘘だと思うなら外して試してみな。
ここまででリターンされなかったら引数で要求されたところまで盤面の動きを再現できなかったことを意味しますから、self を返します。
# reaching here, you cannot trace
logger.warning('FAILED TO BACK')
return self
そう、
self を
返してるんです。False ではありません。いや、False でもいいのかもしれませんが、私がコーディングした時 False でバグった経緯がありますので self をリターンしています。