1
1
from array import array
2
2
from bisect import bisect
3
3
from collections import Counter
4
- from itertools import permutations , product
4
+ from itertools import combinations_with_replacement , permutations
5
5
from operator import itemgetter , attrgetter
6
6
import functools
7
7
import random
@@ -46,9 +46,15 @@ class Shape:
46
46
``accept`` function of a simulation, for example..
47
47
"""
48
48
49
- JOKER = "x"
50
- TABLE = {JOKER : - 1 , "t" : 10 , "j" : 11 , "q" : 12 , "k" : 13 , "(" : "(" , ")" : ")" }
51
- TABLE .update ({str (n ): n for n in range (10 )})
49
+ _str_to_val = {
50
+ "x" : - 1 , "t" : 10 , "j" : 11 , "q" : 12 , "k" : 13 , "(" : "(" , ")" : ")" ,
51
+ ** {str (n ): n for n in range (10 )}}
52
+ _all_shapes = [
53
+ (s , sh - s , shd - sh , len (Rank ) - shd )
54
+ for s , sh , shd
55
+ in combinations_with_replacement (range (len (Rank ) + 1 ), len (Suit ) - 1 )
56
+ ]
57
+ _shape_to_index = {shape : idx for idx , shape in enumerate (_all_shapes )}
52
58
_cls_cache = {}
53
59
54
60
def __new__ (cls , init = None ):
@@ -57,30 +63,30 @@ def __new__(cls, init=None):
57
63
return cls ._cls_cache [init ]
58
64
except KeyError :
59
65
self = object .__new__ (cls )
60
- self .table = array ("b" )
61
- self .table .fromlist ([0 ] * ( len (Rank ) + 1 ) ** len ( Suit ))
66
+ self ._table = array ("b" )
67
+ self ._table .fromlist ([0 ] * len (cls . _all_shapes ))
62
68
self .min_ls = [len (Rank ) for _ in Suit ]
63
69
self .max_ls = [0 for _ in Suit ]
64
70
self ._op_cache = {}
65
71
if init :
66
- self .insert ([self .TABLE [char .lower ()] for char in init ])
72
+ self .insert ([self ._str_to_val [char .lower ()] for char in init ])
67
73
cls ._cls_cache [init ] = self
68
74
return self
69
75
70
76
@classmethod
71
77
def from_table (cls , table , min_max_hint = None ):
72
78
"""Initialize from a table."""
73
79
self = cls ()
74
- self .table = array ("b" )
75
- self .table .fromlist (list (table ))
80
+ self ._table = array ("b" )
81
+ self ._table .fromlist (list (table ))
76
82
if min_max_hint is not None :
77
83
self .min_ls , self .max_ls = min_max_hint
78
84
else :
79
85
self .min_ls = [len (Rank ) for _ in Suit ]
80
86
self .max_ls = [0 for _ in Suit ]
81
- for nonflat in product ( * [ range ( len ( Rank ) + 1 ) for _ in Suit ] ):
82
- if self .table [ self . _flatten ( nonflat ) ]:
83
- for dim , coord in enumerate (nonflat ):
87
+ for idx , shape in enumerate ( cls . _all_shapes ):
88
+ if self ._table [ idx ]:
89
+ for dim , coord in enumerate (shape ):
84
90
self .min_ls [dim ] = min (self .min_ls [dim ], coord )
85
91
self .max_ls [dim ] = max (self .max_ls [dim ], coord )
86
92
return self
@@ -89,28 +95,21 @@ def from_table(cls, table, min_max_hint=None):
89
95
def from_cond (cls , func ):
90
96
"""Initialize from a shape-accepting function."""
91
97
self = cls ()
92
- for nonflat in product ( * [ range ( len ( Rank ) + 1 ) for _ in Suit ] ):
93
- if sum ( nonflat ) == len ( Rank ) and func (* nonflat ):
94
- self .table [ self . _flatten ( nonflat ) ] = True
95
- for dim , coord in enumerate (nonflat ):
98
+ for idx , shape in enumerate ( cls . _all_shapes ):
99
+ if func (* shape ):
100
+ self ._table [ idx ] = True
101
+ for dim , coord in enumerate (shape ):
96
102
self .min_ls [dim ] = min (self .min_ls [dim ], coord )
97
103
self .max_ls [dim ] = max (self .max_ls [dim ], coord )
98
104
return self
99
105
100
- @staticmethod
101
- def _flatten (index ):
102
- """Transform a 4D index into a 1D index."""
103
- s , h , d , c = index
104
- mul = len (Rank ) + 1
105
- return ((((s * mul + h ) * mul ) + d ) * mul ) + c
106
-
107
106
def _insert1 (self , shape , safe = True ):
108
107
"""Insert an element, possibly with "x" but no "()" terms."""
109
108
jokers = any (l == - 1 for l in shape )
110
109
pre_set = sum (l for l in shape if l >= 0 )
111
110
if not jokers :
112
111
if pre_set == len (Rank ):
113
- self .table [self ._flatten ( shape ) ] = 1
112
+ self ._table [self ._shape_to_index [ shape ] ] = 1
114
113
for suit in Suit :
115
114
self .min_ls [suit ] = min (self .min_ls [suit ], shape [suit ])
116
115
self .max_ls [suit ] = max (self .max_ls [suit ], shape [suit ])
@@ -143,7 +142,7 @@ def insert(self, it, acc=()):
143
142
144
143
def __contains__ (self , int_shape ):
145
144
"""Check if the given shape is included."""
146
- return self .table [self ._flatten ( int_shape ) ]
145
+ return self ._table [self ._shape_to_index [ int_shape ] ]
147
146
148
147
def __call__ (self , hand ):
149
148
"""Check if the shape of the given hand is included."""
@@ -155,7 +154,7 @@ def __add__(self, other):
155
154
return self ._op_cache ["+" , other ]
156
155
except KeyError :
157
156
table = array ("b" )
158
- table .fromlist ([x or y for x , y in zip (self .table , other .table )])
157
+ table .fromlist ([x or y for x , y in zip (self ._table , other ._table )])
159
158
min_ls = [min (self .min_ls [suit ], other .min_ls [suit ])
160
159
for suit in Suit ]
161
160
max_ls = [max (self .max_ls [suit ], other .max_ls [suit ])
@@ -171,7 +170,7 @@ def __sub__(self, other):
171
170
except KeyError :
172
171
table = array ("b" )
173
172
table .fromlist (
174
- [x and not y for x , y in zip (self .table , other .table )])
173
+ [x and not y for x , y in zip (self ._table , other ._table )])
175
174
result = Shape .from_table (table , (self .min_ls , self .max_ls ))
176
175
self ._op_cache ["-" , other ] = result
177
176
return result
0 commit comments