Skip to content

Commit

Permalink
Merge pull request #41 from Ledenel/export-paifu-demo
Browse files Browse the repository at this point in the history
Export paifu demo
  • Loading branch information
Ledenel authored Jan 28, 2020
2 parents 690a3e3 + 8dfabf7 commit 3abcff6
Show file tree
Hide file tree
Showing 14 changed files with 439 additions and 169 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ bitstruct = "*"
requests = "*"
jinja2 = "*"
loguru = "*"
pandas = "*"

[requires]
python_version = "3.8"
73 changes: 62 additions & 11 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ with open(filename, "w", encoding='utf-8') as f:
f.write(results)
```

## universe-paifu-convert

This converter extract paifus as a universal format, to csv files for easy analysis.

Now support tenhou.net only.

**IT IS JUST A DEMO TO SHOW THE CONCEPTS, TRANSLATED COMMANDS AND STATES ARE NEITHER COMPLETE NOR CORRECT YET!**

Type your tenhou.net log url (like`http://tenhou.net/0/?log=2019012600gm-0089-0000-100908f0&tw=0`).
## Testing

you could run test by executing `pip install .[test]` at root dir, this would install all dependence for you.
Expand Down
118 changes: 68 additions & 50 deletions mahjong/record/universe/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,14 @@
from collections import namedtuple
from typing import List, TypeVar, Callable, Iterable

import numpy
import pandas
from loguru import logger

from mahjong.record.universe.property_manager import prop_manager
from mahjong.record.universe.format import View, Update


def assertion(func):
def _check_assert(x):
try:
func(x)
return True
except AssertionError:
return False

return _check_assert


class PropertyTypeManager:
def __init__(self):
self._checker = {}
self._default_value = {}

def get_default(self, view: View):
view_typ = view.type
for typ, default_ctor in self._default_value.items():
if view_typ in typ:
return default_ctor()

def assertion_check(self, name):
def _assertion_wrapper(func):
checker = assertion(func)
self._checker[name] = checker
return checker

return _assertion_wrapper

def register_default_ctor(self, name):
def _default_value_wrapper(func):
self._default_value[name] = func

return _default_value_wrapper


def is_empty(x):
return x is None or x != x

Expand All @@ -67,18 +33,22 @@ def scope(self):
return self.view_property.scope



command_field_names = [
"timestamp",
"scope",
"sub_scope_id",
"property",
"update_method",
"value",
]
_Game_command = namedtuple(
"GameCommand_",
field_names=[
"timestamp",
"scope",
"sub_scope_id",
"property",
"update_method",
"value",
]
field_names=command_field_names
)

command_field_names_set = set(command_field_names)


class GameCommand:
def __init__(self, *, prop: View, update: Update, sub_scope=None, value=None, timestamp=None):
self.timestamp = timestamp
Expand Down Expand Up @@ -106,30 +76,79 @@ def __str__(self):
def __repr__(self):
return "{%s}" % str(self)

@staticmethod
def clean(pandas_dataframe):
return pandas_dataframe.apply(GameCommand.pandas_columns_clean, axis="columns")


@staticmethod
def to_dataframe(command_list):
return pandas.DataFrame(
(x.to_record() for x in command_list),
)

@staticmethod
def read_clean_csv(csv_path):
return GameCommand.clean(pandas.read_csv(csv_path))

@staticmethod
def pandas_columns_clean(row):
# remove index
row = row[command_field_names]
series_ctor = type(row)
record = _Game_command(**row)
command = GameCommand.from_record(record)
target = numpy.array(command.to_raw_record(), dtype=object)
target_series = series_ctor(target)
target_series.index = command_field_names
return target_series

def to_raw_record(self):
return _Game_command(
timestamp=self.timestamp,
scope=self.prop.scope,
sub_scope_id=self.sub_scope_id,
property=self.prop.view_property,
update_method=self.prop.update_method,
value=self.value,
)

@staticmethod
def from_raw_record(record: _Game_command):
return GameCommand(
prop=record.property,
update=record.update_method,
sub_scope=norm_empty(record.sub_scope_id),
value=norm_empty(record.value),
timestamp=norm_empty(record.timestamp),
)

def to_record(self):
return _Game_command(
timestamp=self.timestamp,
scope=self.prop.scope.name,
sub_scope_id=self.sub_scope_id,
property=self.prop.view_property.name,
update_method=self.prop.update_method.name,
value=self.value,
value=prop_manager.to_str(self.value, self.prop.view_property),
)

@staticmethod
def from_record(record: _Game_command):
view = View.by_name(record.scope)[record.property]
return GameCommand(
prop=View.by_name(record.scope)[record.property],
prop=view,
update=Update[record.update_method],
sub_scope=norm_empty(record.sub_scope_id),
value=norm_empty(record.value),
value=prop_manager.from_str(record.value, view=view),
timestamp=norm_empty(record.timestamp),
)


EventT = TypeVar('EventT')
EventTransform = Callable[[EventT], Iterable[GameCommand]]


class CommandTranslator:
def __init__(self):
self.defaults = []
Expand Down Expand Up @@ -161,4 +180,3 @@ def __call__(self, event: EventT) -> List[GameCommand]:
else:
logger.warning("event <{}> is not transformed to game commands.", event)
return []

Loading

0 comments on commit 3abcff6

Please sign in to comment.