Created
January 29, 2017 09:35
-
-
Save KTakahiro1729/49ef153bb5298a8ed3827cf717cec7dd to your computer and use it in GitHub Desktop.
hanico.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import datetime, re | |
from math import inf | |
import hanictionary as hDs | |
''' | |
hanicoモジュールは箱庭諸島の近況ログを解析するためのモジュールです。 | |
通常、近況ログは各島のマイページからコピペできます。 | |
hanicoでは、近況ログを階層構造を持つ物として扱います。 | |
すなわち、 | |
- ラインが集まった物であるログ | |
- ログが集まった物であるイベントチャンク | |
- イベントチャンクが集まった物であるターンチャンク | |
-ターンチャンクが集まったパストパーフェクト | |
のように | |
- ライン | |
- ログ | |
- イベントチャンク | |
- ターンチャンク | |
- パストパーフェクト | |
の5階層に分かれています。 | |
最下位の階層は「ライン」、Lineです。 | |
ラインはstring型と実質同じなので、hanicoで新たにクラスを作ってはいません。 | |
ラインは、近況ログ中の各行です。 | |
厳密に言うなら、前の改行文字と後の改行文字とに挟まれた部分こそがラインです。 | |
ラインと後述するログの違いは分かりにくいですが、それはログの説明でします。 | |
ラインが集まると「ログ」、Logになります。 | |
ログはhanicoにおいて、Log型が定義されています。 | |
ログは、島のメニューから直接見たときに、細い線で区切られている各部分です。 | |
これはラインと紛らわしいのですが、海戦の戦闘ログのように、ログが複数行になっている物を考えるとわかりやすいと思います。 | |
どのラインを同一ログと見なすかは、hanictionaryモジュール内のlogDictを参照してください。 | |
イベントチャンク、Event Chunkが次の階層です。 | |
イベントチャンクはhanicoにおいて、EChunk型が定義されています。 | |
イベントチャンクは、同じ出来事の一かたまりのログを指します。 | |
例えば、ミサイル発射のログは、全弾の結果が別々のログで表示されますが、ミサイル発射、という同じコマンドの実行結果ですので、同一ログEChunk内のログと見なされます。 | |
どのログを同一イベントチャンクと見なすかは、hanictionaryモジュール内のeChunkDictを参照してください。 | |
ターンチャンク、Turn Chunkはイベントチャンクの集まりです。 | |
ターンチャンクはhanicoにおいて、TChunk型が定義されています。 | |
ターンチャンク「TChunkヘッダー(「■ターン」から始まり、ターンと実行時間の情報を掲載するイベントチャンク)を区切りにしています。 | |
パストパーフェクトPastPerfectは最上位の階層です。 | |
パストパーフェクトはhanicoにおいて、PastPerfect型が定義されています。 | |
ターンチャンクの集まり、つまり近況ログ全体を格納します。 | |
通常、与えられたラインの配列は全て読みますが、tChunkのターン数に不整合、つまり、一個上に記載されたtChunkよりターン数が多い/OR2以上少ない場合は、読み込みをそこで中止します。 | |
''' | |
def readLogFile(fileName,encoding='utf-8', msg = True, year = datetime.datetime.today().year, islandName = '自島'): | |
'''与えられたファイル名からログを抽出し、Log型で返す''' | |
txt = open(fileName, 'rt', encoding = encoding) | |
lineList = [line.replace('\n','') for line in txt.readlines()] | |
result = PastPerfect(lineList, year, msg, islandName) | |
return result | |
class Log(): | |
@staticmethod | |
def firstElement(lineList): | |
describe = None | |
for key in hDs.logDict: | |
templateLineList = hDs.logDict[key] | |
if re.fullmatch(''.join(templateLineList),''.join(lineList[:len(templateLineList)])): | |
describe = key | |
log = lineList[:len(templateLineList)] | |
break | |
else: | |
log = lineList[:1] | |
return log, describe | |
def __init__(self, redundantLineList): | |
self.__element, self.describe = self.firstElement(redundantLineList) | |
def lineList(self): | |
return self.__element | |
class EChunk(): | |
@staticmethod | |
def firstElement(lineList): | |
logList = [Log(lineList)] | |
for key in hDs.eChunkDict: | |
if logList[0].describe in hDs.eChunkDict[key].startLog: | |
eChunkType = key | |
break | |
else: | |
eChunkType = None | |
return[logList, None] | |
lineList = lineList[len(logList[0].lineList()):] | |
while lineList: | |
log = Log(lineList) | |
lineList = lineList[len(log.lineList()):] | |
if log.describe in hDs.eChunkDict[eChunkType].contentLog: | |
logList.append(log) | |
continue | |
if log.describe in hDs.eChunkDict[eChunkType].endLog: | |
logList.append(log) | |
break | |
else: | |
break | |
return logList, eChunkType | |
def __init__(self, redundantLineList): | |
self.__element, self.describe = self.firstElement(redundantLineList) | |
def logList(self): | |
return self.__element | |
def lineList(self): | |
return [line for log in self.logList() for line in log.lineList()] | |
class TChunk(): | |
@staticmethod | |
def firstElement(lineList): | |
if Log(lineList[:1]).describe != '■TChunkヘッダー': | |
print(Log(lineList[:1]).describe) | |
raise Exception('最初の行('+lineList[0]+')がTChunkヘッダーではありません。') | |
tChunk = [EChunk(lineList[:1])] | |
lineList = lineList[1:] | |
while lineList: | |
if Log(lineList[:1]).describe == '■TChunkヘッダー': | |
break | |
eChunk = EChunk(lineList) | |
tChunk.append(eChunk) | |
lineList = lineList[len(eChunk.lineList()):] | |
if len(tChunk) == 1: | |
tChunkType = None | |
else: | |
tChunkType = tChunk[1].describe | |
return tChunk, tChunkType | |
@staticmethod | |
def extractDatetime(header, year): | |
stripPart = ' '.join(header.split()[2:]) | |
result = datetime.datetime.strptime(stripPart, '%m/%d %H:%M').replace(year = year) | |
return result | |
@staticmethod | |
def extractTurn(header): | |
return int(header.split()[0][4:]) | |
def __init__(self, redundantLineList, year = datetime.datetime.today().year): | |
self.__element, self.describe= self.firstElement(redundantLineList) | |
self.header = self.__element[0].lineList()[0] | |
self.contents = self.__element[1:] | |
self.datetime = self.extractDatetime(self.header, year) | |
self.turn = self.extractTurn() | |
def eChunkList(self): | |
return self.__element | |
def logList(self): | |
return [log for eChunk in self.eChunkList() for log in eChunk.logList()] | |
def lineList(self): | |
return [line for log in self.logList() for line in log.lineList()] | |
def extractTurn(self): | |
return int(self.header.split()[0][4:]) | |
class PastPerfect(): | |
@staticmethod | |
def firstElement(lineList, msg, year, islandName): | |
tChunkList = [] | |
assumedTChunkNum = sum([1 for line in lineList if line[:4] == '■ターン']) | |
if msg: | |
print('推定{0}個のターンチャンクがあります。'.format(assumedTChunkNum)) | |
count = 0 | |
earliestDatetime, earliestTurn = datetime.datetime.max, inf | |
while lineList: | |
tChunk = TChunk(lineList, year) | |
count += 1 | |
if (earliestTurn - tChunk.turn) not in (0, 1, inf) : | |
if msg: | |
print('\n全{0}(/{1})個のターンチャンクを読み込みました。'.format(count - 1, assumedTChunkNum)) | |
print('※ターン順序に不整合があったため、{1}個目の以降のターンチャンクの読み込みを中止しました。\n\t{0}個目のターン数: {2}\n\t{1}個目のターン数: {3}'.format(count - 1, count,earliestTurn, tChunk.turn)) | |
break | |
if earliestDatetime < tChunk.datetime: | |
if msg: | |
print('\n※年をまたぎました。\n\t~{0}個目: {2}年\n\t{1}個目~: {3}年'.format(count - 1, count, year, year - 1)) | |
year -= 1 | |
tChunk.datetime = tChunk.datetime.replace(year = year) | |
if msg: | |
print('\r{0}/{1}個のターンチャンクが読み込まれました。'.format(count, assumedTChunkNum),end = '') | |
earliestDatetime, earliestTurn = [tChunk.datetime, tChunk.turn] | |
tChunkList.append(tChunk) | |
lineList = lineList[len(tChunk.lineList()):] | |
else: | |
print('\n全{0}のターンチャンクが読み込ました。'.format(count)) | |
return tChunkList, islandName | |
def __init__(self, lineList, msg = True, year = datetime.datetime.today().year, islandName = '自島'): | |
self.__element, self.describe = self.firstElement(lineList, msg, year, islandName) | |
def tChunkList(self): | |
return self.__element | |
def eChunkList(self): | |
return [eChunk for tChunk in self.tChunkList() for eChunk in tChunk.eChunkList()] | |
def logList(self): | |
return [log for eChunk in self.eChunkList() for log in eChunk.logList()] | |
def lineList(self): | |
return [line for log in self.logList() for line in log.lineList()] | |
def main(): | |
from pprint import pprint | |
testLineList = [ | |
'■ターン18313 - 1/22 23:02', | |
'於:a島', | |
'攻撃(雷撃):a島所属 潜水艦(9,1)', | |
'目標:b島所属 戦艦(10,1)', | |
'戦果:182のダメージ!(経験値:+182)', | |
'■ターン18312 - 1/21 21:01', | |
'(1,9)で埋め立てが行われました。', | |
'台風上陸!!', | |
'(2,9)の農場は台風で飛ばされました。', | |
'■ターン18311 - 1/21 21:00', | |
'(2,1)で一括地ならしが行われました。', | |
'(6,1)で一括地ならしが行われました。', | |
'(7,1)で一括地ならしが行われました。', | |
'(8,1)で一括地ならしが行われました。', | |
'(8,2)で一括地ならしが行われました。', | |
'(8,4)で一括地ならしが行われました。', | |
'(9,3)で一括地ならしが行われました。', | |
'(9,4)で一括地ならしが行われました。', | |
'(10,3)で一括地ならしが行われました。', | |
'■ターン18312 - 1/21 21:01', | |
'(1,9)で埋め立てが行われました。', | |
'台風上陸!!', | |
'(2,9)の農場は台風で飛ばされました。', | |
] | |
pprint(Log(testLineList).lineList()) | |
pprint(EChunk(testLineList).lineList()) | |
pprint(TChunk(testLineList).lineList()) | |
pprint(PastPerfect(testLineList).describe) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment