Skip to content

Instantly share code, notes, and snippets.

@KTakahiro1729
Created January 29, 2017 09:35
Show Gist options
  • Save KTakahiro1729/49ef153bb5298a8ed3827cf717cec7dd to your computer and use it in GitHub Desktop.
Save KTakahiro1729/49ef153bb5298a8ed3827cf717cec7dd to your computer and use it in GitHub Desktop.
hanico.py
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