2-4 文字を変換する機能を設定する
IO.py ファイルをご覧ください。先頭のコメントの下に
from config import *
ってのがありますね。「config.py のファイルの中身は好き勝手つかっていいよ」って意味です。普通の import なら config. をつけなきゃいけないところを、一切つけなくていいんです。
特に a, b, c, ... って file 番号を表すアルファベットがあるでしょう?あんなもんに config. って一々つけてちゃ気が持ちませんよ。せっかくコードを書きやすく読みやすいように定数の名前つけたのに、 config.f - config.d とか、そんなん
書いてられっか。
~~~~~~~~~~~~~~~
直下で 2-1 で作った setLogger 関数を使って local_logger というロガーを宣言しています。
local_logger = setLogger(__name__)
「このモジュール(ファイル)では原則 local_logger を使いましょう」という意図で宣言しています。引数の __name__ にはこのモジュールの名前がそのまま入ります。つまりロガーの名前は IO となります。
~~~~~~~~~~~~~~~
その下、 ToggleType 関数をご覧ください。
def ToggleType(target, logger=local_logger):
# piece ID -> piece letter
if type(target) is int:
if target == EMPTY:
return ' '
elif target == P * BLACK:
return '♙'
elif target == R * BLACK:
return '♖'
elif target == N * BLACK:
return '♘'
elif target == B * BLACK:
return '♗'
elif target == Q * BLACK:
return '♕'
elif target == K * BLACK:
return '♔'
elif target == P * WHITE:
return '♟'
elif target == R * WHITE:
return '♜'
elif target == N * WHITE:
return '♞'
elif target == B * WHITE:
return '♝'
elif target == Q * WHITE:
return '♛'
elif target == K * WHITE:
return '♚'
# invalid target value
else:
logger.error('UNEXPECTED INPUT VALUE of A PIECE into IO.ToggleType')
return False
# str -> int
elif type(target) is str:
# a str number -> int
if target.isdecimal():
return int(target)
# file id
elif ord('a') <= ord(target) <= ord('h'):
return ord(target) - ord('a') + 1
# the kind of piece -> piece no.
elif target == 'P':
return P
elif target == 'R':
return R
elif target == 'N':
return N
elif target == 'B':
return B
elif target == 'Q':
return Q
elif target == 'K':
return K
# invalid character
else:
logger.error('UNEXPECTED INPUT into IO.ToggleType')
return False
# unexpected type
else:
logger.error('UNEXPECTED INPUT TYPE into IO.ToggleType')
return False
board の値から盤面を表示するときに使う '♟' などの特殊文字を出力したり、'K' などの駒を表す文字から駒の番号をリターンする機能、それから 'c3' など、マスの位置を表す文字を数字にする機能を実装しています。
基本的に盤面をプレーヤーに見せるときと、プレーヤーが入力した棋譜で「どの駒がどこに動いているかな」という情報を扱いやすい形にするのが主な役割となります。
def ToggleType(target, logger=local_logger):
引数の target が「board の値は何なの?」「どこのマスを指してるの?」といった情報を表します。一つの変数に複数の意味があると、
この target は何を表しているんだ??
となってしまいそうですが、target の型に応じて場合わけしていきますのでご心配なく。
それからもう一つ、ロガーを引数にとっていますよね。これは「このロガーを使ってログをとってくれ」という要望に答えられるようにするためです。詳しくはこちらで力説されていますので、是非ご覧ください。
ログ出力のための print と import logging はやめてほしい
デフォルト引数には早速 local_logger を採用しています。あの冒頭で宣言したやつね。
~~~~~~~~~~~~~~~
さて、関数の本題に入りましょう。まずは target が扱うものを一度整理しましょうか。
board[file][rank] の値 | 駒はあるか、白黒どちらの駒か、駒の種類は何か | int |
rank を表す文字 | ユーザーが入力したのはどの rank か(1 スタート) | str |
file を表す文字 | ユーザーが入力したのはどの file か(1 スタート) | str |
駒を表す文字 | ユーザーが入力したのはどの駒か | str |
ユーザーからすればリストのインデックスは 0 から始めなきゃいけないことなんて知ったこっちゃありませんから、rank も file も 1 スタートでお願いします。
~~~~~~~~~~~~~~~
まずは int 型のときは駒情報しかないことを使って、board の値から駒の特殊文字をリターンする場合をコーディングしていきます。
幸い 1-2 でうまく設定したおかげで board の値と白黒の各駒は 1 対 1 で対応してくれてます。プレーヤー、種類に応じて特殊文字を使い分けてください。
# piece ID -> piece letter
if type(target) is int:
if target == EMPTY:
return ' '
elif target == P * BLACK:
return '♙'
elif target == R * BLACK:
return '♖'
elif target == N * BLACK:
return '♘'
elif target == B * BLACK:
return '♗'
elif target == Q * BLACK:
return '♕'
elif target == K * BLACK:
return '♔'
elif target == P * WHITE:
return '♟'
elif target == R * WHITE:
return '♜'
elif target == N * WHITE:
return '♞'
elif target == B * WHITE:
return '♝'
elif target == Q * WHITE:
return '♛'
elif target == K * WHITE:
return '♚'
# invalid target value
else:
logger.error('UNEXPECTED INPUT VALUE of A PIECE into IO.ToggleType')
return False
それと、私の実行環境では中抜きの方が黒く見えているのですが、そうでない場合は
中抜きは白の方がいいな
とか、ご自分の実行環境に合わせてプログラミングしてください。
else で弾かれたところ、駒を表さない int はこの先出番はありませんから、この時点で「引数おかしい」False をリターンしてください。
~~~~~~~~~~~~~~~
今度は str 型の target を場合わけしてコーディングします。
# str -> int
elif type(target) is str:
# a str number -> int
if target.isdecimal():
return int(target)
# file id
elif ord('a') <= ord(target) <= ord('h'):
return ord(target) - ord('a') + 1
# the kind of piece -> piece no.
elif target == 'P':
return P
elif target == 'R':
return R
elif target == 'N':
return N
elif target == 'B':
return B
elif target == 'Q':
return Q
elif target == 'K':
return K
# invalid character
else:
logger.error('UNEXPECTED INPUT into IO.ToggleType')
return False
まず数字が入力されたら、str を int にしてリターンしましょう。rank を表す分ですね。
# a str number -> int
if target.isdecimal():
return int(target)
ここは
リストのインデックスで使うために 1 を引いて...
とか考えて今ここで下手に手を加えるよりも、このリターンされた数字を使うところでどのように手を加えるか判断するのが良さそうです。
file は a から h の小文字のアルファベットで表します。この範囲に target があるかは ord 関数で UNICODE を取得して比較しています。
# file id
elif ord('a') <= ord(target) <= ord('h'):
return ord(target) - ord('a') + 1
アルファベットの UNICODE はアルファベット順に付けられていますから、純粋に a の UNICODE から h の UNICODE までの間に入っていれば、target 自身も a から h の中にあることがわかります。
また UNICODE は 16 進数の数であらわされますから、二つの文字の差を取ればそのまま文字番号の差になってくれます。
16 進数とかいうやつ、そのまま普通の数みたいに使ってええの?
数の書き方が違うだけで数の意味は同じなので、全く問題ありません。ただし 1-2 では a を 1 としていますから、計算するとき最後に +1 を忘れないこと。
~~~~~~~~~~~~~~~
最後にユーザーが入力する駒を表す文字を判読します。具体的には
ポーン:PAWN | P |
ルーク:ROOK | R |
ナイト:KNIGHT | N |
ビショップ:BISHOP | B |
クイーン:QUEEN | Q |
キング:KING | K |
を判読して、2-2 で決めた駒の種類を表す数字に変換、リターンします。
数字...?P とか R とかが?
2-2 で値を入れてますよ。
# the kind of piece -> piece no.
elif target == 'P':
return P
elif target == 'R':
return R
elif target == 'N':
return N
elif target == 'B':
return B
elif target == 'Q':
return Q
elif target == 'K':
return K
これにて target が正常な場合は終了です。
~~~~~~~~~~~~~~~
さて、target がここまで残ったらもう手のつけようがございません。「引数の値がおかしい」ということで False をリターンします。
もっと言えば「型はあってるけど中身がおかしい」というのと「型の時点で受け付けられない」というのと、2 種類のエラーをログにとってあげるのが親切なんじゃないでしょうか。
# invalid character
else:
logger.error('UNEXPECTED INPUT into IO.ToggleType')
return False
# unexpected type
else:
logger.error('UNEXPECTED INPUT TYPE into IO.ToggleType')
return False
ここまでで ToggleType は終わりました。下に instruction が残っていますが、こちらは Stage 7 で扱います。
いや、「焦らすのはよくない」ってみんなさんおっしゃいますけどね、私こんな関数最初から考えてたんじゃないんですから、むしろ説明としては自然でしょうよ。
欲しがりはいけませんよ、奥さん。