absfuyu 3.0.0__py3-none-any.whl → 3.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of absfuyu might be problematic. Click here for more details.

absfuyu/fun/tarot.py CHANGED
@@ -4,8 +4,8 @@ Absfuyu: Tarot
4
4
  Tarot stuff
5
5
 
6
6
 
7
- Version: 1.0.2
8
- Date updated: 27/11/2023 (dd/mm/yyyy)
7
+ Version: 1.0.3
8
+ Date updated: 01/01/2024 (dd/mm/yyyy)
9
9
 
10
10
  Usage:
11
11
  ------
@@ -23,8 +23,8 @@ __all__ = ["Tarot", "TarotCard"]
23
23
  import random
24
24
  from typing import Dict, List
25
25
 
26
- from absfuyu.core import DATA_PATH
27
26
  from absfuyu.logger import logger
27
+ from absfuyu.pkg_data import DataList
28
28
  from absfuyu.util.pkl import Pickler
29
29
 
30
30
 
@@ -60,7 +60,7 @@ class Tarot:
60
60
  """Tarot data"""
61
61
 
62
62
  def __init__(self) -> None:
63
- self.data_location = DATA_PATH.joinpath("tarot.pkl")
63
+ self.data_location = DataList.TAROT
64
64
 
65
65
  def __str__(self) -> str:
66
66
  return f"{self.__class__.__name__}()"
@@ -68,9 +68,10 @@ class Tarot:
68
68
  def __repr__(self) -> str:
69
69
  return self.__str__()
70
70
 
71
- def _load(self) -> List[TarotCard]:
71
+ @property
72
+ def tarot_deck(self) -> List[TarotCard]:
72
73
  """
73
- Load tarot pickle data
74
+ Load pickled tarot data
74
75
 
75
76
  :rtype: list[TarotCard]
76
77
  """
@@ -97,10 +98,11 @@ class Tarot:
97
98
  TarotCard
98
99
  Random Tarot card
99
100
  """
100
- return random.choice(self._load())
101
+ return random.choice(self.tarot_deck)
101
102
 
102
103
 
103
104
  # Run
104
105
  ###########################################################################
105
106
  if __name__ == "__main__":
107
+ logger.setLevel(10)
106
108
  pass
@@ -2,24 +2,22 @@
2
2
  Game: Tic Tac Toe
3
3
  -----------------
4
4
 
5
- Version: 2.0.1
6
- Date updated: 22/11/2023 (mm/dd/yyyy)
5
+ Version: 2.0.2
6
+ Date updated: 04/12/2023 (mm/dd/yyyy)
7
7
  """
8
8
 
9
9
 
10
10
  # Module level
11
11
  ###########################################################################
12
- __all__ = [
13
- "TicTacToe", "GameMode"
14
- ]
12
+ __all__ = ["TicTacToe", "GameMode"]
15
13
 
16
14
 
17
15
  # Library
18
16
  ###########################################################################
19
- from collections import namedtuple
17
+ # from collections import namedtuple
20
18
  import random
21
19
  import time
22
- from typing import Dict, List, Union
20
+ from typing import Dict, List, NamedTuple, Union
23
21
 
24
22
  from absfuyu.core import CLITextColor
25
23
 
@@ -27,7 +25,12 @@ from absfuyu.core import CLITextColor
27
25
  # Class
28
26
  ###########################################################################
29
27
  BoardGame = List[List[str]]
30
- Pos = namedtuple("Pos", ["row", "col"]) # Position
28
+ # Pos = namedtuple("Pos", ["row", "col"]) # Position
29
+ class Pos(NamedTuple):
30
+ """Position"""
31
+ row: int
32
+ col: int
33
+
31
34
 
32
35
  class GameMode:
33
36
  ONE_V_ONE = "1v1"
@@ -37,18 +40,18 @@ class GameMode:
37
40
 
38
41
  class TicTacToe:
39
42
  """Tic Tac Toe game"""
43
+
40
44
  def __init__(
41
- self,
42
- game_size: int = 3,
43
- *,
44
- x: str = "X",
45
- o: str = "O",
46
- blank: str = " ",
47
- position_split_symbol: str = ",",
48
- end_break_word: str = "END",
49
- welcome_message: bool = True
50
-
51
- ) -> None:
45
+ self,
46
+ game_size: int = 3,
47
+ *,
48
+ x: str = "X",
49
+ o: str = "O",
50
+ blank: str = " ",
51
+ position_split_symbol: str = ",",
52
+ end_break_word: str = "END",
53
+ welcome_message: bool = True,
54
+ ) -> None:
52
55
  """
53
56
  :param game_size: Board size (Default: 3x3)
54
57
  :param x: X symbol
@@ -76,56 +79,57 @@ class TicTacToe:
76
79
 
77
80
  def __str__(self) -> str:
78
81
  return f"{self.__class__.__name__}(game_size={self.row_size})"
82
+
79
83
  def __repr__(self) -> str:
80
84
  return self.__str__()
81
-
85
+
82
86
  # Game
83
87
  def _gen_board(self) -> BoardGame:
84
88
  """
85
89
  Generate board game
86
90
  """
87
91
  board = [
88
- [self.BLANK for _ in range(self.row_size)]
89
- for _ in range(self.col_size)
92
+ [self.BLANK for _ in range(self.row_size)] for _ in range(self.col_size)
90
93
  ]
91
94
  return board
92
-
95
+
93
96
  def _check_state(self) -> Dict[str, Union[str, int]]:
94
97
  """
95
98
  Check game winning state
96
-
97
- Return:
98
- ---
99
- X | O | BLANK
99
+
100
+ Returns
101
+ -------
102
+ dict[str, str | int]
103
+ ``X`` | ``O`` | ``BLANK``
100
104
  """
101
105
 
102
106
  # Check rows
103
107
  for row in range(self.row_size):
104
108
  if len(set(self.board[row])) == 1:
105
109
  key = list(set(self.board[row]))[0]
106
- return {"key": key, "location": "row", "pos": row} # modified
110
+ return {"key": key, "location": "row", "pos": row} # modified
107
111
 
108
112
  # Check cols
109
113
  for col in range(self.col_size):
110
114
  temp = [self.board[row][col] for row in range(self.row_size)]
111
115
  if len(set(temp)) == 1:
112
116
  key = list(set(temp))[0]
113
- return {"key": key, "location": "col", "pos": col} # modified
114
-
117
+ return {"key": key, "location": "col", "pos": col} # modified
118
+
115
119
  # Check diagonal
116
120
  diag1 = [self.board[i][i] for i in range(len(self.board))]
117
121
  if len(set(diag1)) == 1:
118
122
  key = list(set(diag1))[0]
119
- return {"key": key, "location": "diag", "pos": 1} # modified
120
-
121
- diag2 = [self.board[i][len(self.board)-i-1] for i in range(len(self.board))]
123
+ return {"key": key, "location": "diag", "pos": 1} # modified
124
+
125
+ diag2 = [self.board[i][len(self.board) - i - 1] for i in range(len(self.board))]
122
126
  if len(set(diag2)) == 1:
123
127
  key = list(set(diag2))[0]
124
- return {"key": key, "location": "diag", "pos": 2} # modified
125
-
128
+ return {"key": key, "location": "diag", "pos": 2} # modified
129
+
126
130
  # Else
127
131
  return {"key": self.BLANK}
128
-
132
+
129
133
  @staticmethod
130
134
  def _print_board(board: BoardGame) -> None:
131
135
  """
@@ -138,7 +142,7 @@ class TicTacToe:
138
142
  for col in range(ncol):
139
143
  print(f"| {board[row][col]} ", end="")
140
144
  print(f"|\n{'+---'*length}+")
141
-
145
+
142
146
  def _win_hightlight(self) -> BoardGame:
143
147
  """
144
148
  Hight light the win by removing other placed key
@@ -165,44 +169,44 @@ class TicTacToe:
165
169
  board[i][i] = detail["key"]
166
170
  else:
167
171
  for i in range(len(board)):
168
- board[i][len(board)-i-1] = detail["key"]
169
-
172
+ board[i][len(board) - i - 1] = detail["key"]
173
+
170
174
  # Output
171
175
  return board
172
-
176
+
173
177
  def _is_blank(self, pos: Pos) -> bool:
174
178
  """Check if current pos is filled"""
175
179
  return self.board[pos.row][pos.col] == self.BLANK
176
180
 
177
181
  @staticmethod
178
- def _convert_bot_output(pos: Pos):
182
+ def _convert_bot_output(pos: Pos) -> Pos:
179
183
  """
180
184
  Turn to real pos by:
181
- - +1 to row and col
182
- - convert into str
185
+
186
+ - +1 to ``row`` and ``col``
187
+ - convert into ``str``
183
188
  """
184
189
  return Pos(pos.row + 1, pos.col + 1)
185
-
186
- def _generate_random_move(self):
190
+
191
+ def _generate_random_move(self) -> Pos:
187
192
  """
188
193
  Generate a random move from board game
189
194
  """
190
195
  while True:
191
196
  output = Pos(
192
- random.randint(0, len(self.board)-1),
193
- random.randint(0, len(self.board)-1)
197
+ random.randint(0, len(self.board) - 1),
198
+ random.randint(0, len(self.board) - 1),
194
199
  )
195
200
  if self._is_blank(output):
196
201
  break
197
202
  return self._convert_bot_output(output)
198
203
 
199
-
200
204
  def play(
201
- self,
202
- game_mode: str = GameMode.ONE_V_BOT,
203
- *,
204
- bot_time: float = 0,
205
- ) -> None:
205
+ self,
206
+ game_mode: str = GameMode.ONE_V_BOT,
207
+ *,
208
+ bot_time: float = 0,
209
+ ) -> None:
206
210
  """
207
211
  Play a game of Tic Tac Toe
208
212
 
@@ -210,7 +214,7 @@ class TicTacToe:
210
214
  ----------
211
215
  game_mode : str
212
216
  Game mode
213
-
217
+
214
218
  bot_time : float
215
219
  Time sleep between each bot move (Default: ``0``)
216
220
  """
@@ -223,63 +227,71 @@ class TicTacToe:
223
227
 
224
228
  # Welcome message
225
229
  if self.welcome_message:
226
- print(f"""\
230
+ print(
231
+ f"""\
227
232
  {CLITextColor.GREEN}Welcome to Tic Tac Toe!
228
233
 
229
234
  {CLITextColor.YELLOW}Rules: Match lines vertically, horizontally or diagonally
230
235
  {CLITextColor.YELLOW}{self.X} goes first, then {self.O}
231
- {CLITextColor.RED}Type '{self.END_BREAK}' to end the game{CLITextColor.RESET}""")
232
-
236
+ {CLITextColor.RED}Type '{self.END_BREAK}' to end the game{CLITextColor.RESET}"""
237
+ )
238
+
233
239
  # Check gamemode
234
240
  _game_mode = [
235
- "1v1", # Player vs player
236
- "1v0", # Player vs BOT
237
- "0v0" # BOT vs BOT
241
+ "1v1", # Player vs player
242
+ "1v0", # Player vs BOT
243
+ "0v0", # BOT vs BOT
238
244
  ]
239
245
  if game_mode not in _game_mode:
240
- game_mode = _game_mode[1] # Force vs BOT
246
+ game_mode = _game_mode[1] # Force vs BOT
241
247
  if game_mode.startswith(GameMode.ONE_V_BOT):
242
248
  BOT = True
243
249
  if game_mode.startswith(GameMode.BOT_V_BOT):
244
250
  BOT = True
245
251
  BOT2 = True
246
-
252
+
247
253
  # Game
248
254
  self._print_board(self.board)
249
255
 
250
256
  place_pos = None
251
257
  while state == self.BLANK and filled < self.row_size**2:
252
258
  print(f"{CLITextColor.BLUE}{current_player}'s turn:{CLITextColor.RESET}")
253
-
254
- try: # Error handling
259
+
260
+ try: # Error handling
255
261
  if (BOT and current_player == self.O) or BOT2:
256
262
  move = self._generate_random_move()
257
263
  str_move = f"{move.row}{self.POS_SPLIT}{move.col}"
258
264
  move = str_move
259
265
  else:
260
- move = input(f"Place {CLITextColor.BLUE}{current_player}{CLITextColor.RESET} at {CLITextColor.BLUE}<row{self.POS_SPLIT}col>:{CLITextColor.RESET} ")
266
+ move = input(
267
+ f"Place {CLITextColor.BLUE}{current_player}{CLITextColor.RESET} at {CLITextColor.BLUE}<row{self.POS_SPLIT}col>:{CLITextColor.RESET} "
268
+ )
261
269
 
262
- if move.upper() == self.END_BREAK: # Failsafe
270
+ if move.upper() == self.END_BREAK: # Failsafe
263
271
  print(f"{CLITextColor.RED}Game ended{CLITextColor.RESET}")
264
272
  break
265
-
273
+
266
274
  move = move.split(self.POS_SPLIT)
267
275
  row = int(move[0])
268
276
  col = int(move[1])
269
- place_pos = Pos(row-1, col-1)
277
+ place_pos = Pos(row - 1, col - 1)
270
278
 
271
279
  if self._is_blank(place_pos):
272
280
  self.board[place_pos.row][place_pos.col] = current_player
273
281
  filled += 1
274
-
275
- else: # User and BOT error
276
- print(f"{CLITextColor.RED}Invalid move, please try again{CLITextColor.RESET}")
282
+
283
+ else: # User and BOT error
284
+ print(
285
+ f"{CLITextColor.RED}Invalid move, please try again{CLITextColor.RESET}"
286
+ )
277
287
  continue
278
-
279
- except: # User error
280
- print(f"{CLITextColor.RED}Invalid move, please try again{CLITextColor.RESET}")
288
+
289
+ except: # User error
290
+ print(
291
+ f"{CLITextColor.RED}Invalid move, please try again{CLITextColor.RESET}"
292
+ )
281
293
  continue
282
-
294
+
283
295
  state = self._check_state()["key"]
284
296
  self._print_board(self.board)
285
297
 
@@ -288,9 +300,9 @@ class TicTacToe:
288
300
  self._print_board(self._win_hightlight())
289
301
 
290
302
  # Change turn
291
- if BOT2: # BOT delay
303
+ if BOT2: # BOT delay
292
304
  time.sleep(bot_time)
293
-
305
+
294
306
  if current_player == self.X:
295
307
  current_player = self.O
296
308
  else:
@@ -303,4 +315,4 @@ class TicTacToe:
303
315
  # Run
304
316
  ###########################################################################
305
317
  if __name__ == "__main__":
306
- pass
318
+ pass
@@ -3,8 +3,8 @@ Absfuyu: Data extension
3
3
  -----------------------
4
4
  Extension for data type such as ``list``, ``str``, ``dict``, ...
5
5
 
6
- Version: 1.9.0
7
- Date updated: 01/12/2023 (dd/mm/yyyy)
6
+ Version: 1.12.3
7
+ Date updated: 23/01/2024 (dd/mm/yyyy)
8
8
 
9
9
  Features:
10
10
  ---------
@@ -30,8 +30,10 @@ __all__ = [
30
30
  # Sub
31
31
  "Pow",
32
32
  "ListREPR",
33
+ "ListNoDunder",
33
34
  "DictBoolTrue",
34
35
  "DictBoolFalse",
36
+ # "DictNoDunder",
35
37
  ]
36
38
 
37
39
 
@@ -42,7 +44,8 @@ from itertools import accumulate, chain, groupby
42
44
  import math
43
45
  import operator
44
46
  import random
45
- from typing import Any, Dict, List, Optional, Union
47
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
48
+ from sys import version_info as python_version
46
49
 
47
50
  from absfuyu.general.generator import Generator, Charset
48
51
  from absfuyu.logger import logger, _compress_list_for_print
@@ -109,6 +112,14 @@ class ListREPR(list):
109
112
  return _compress_list_for_print(self, 9)
110
113
 
111
114
 
115
+ class ListNoDunder(List[str]):
116
+ """Use with ``object.__dir__()``"""
117
+
118
+ def __repr__(self) -> str:
119
+ out = [x for x in self if not x.startswith("__")]
120
+ return out.__repr__()
121
+
122
+
112
123
  class DictBoolTrue(Dict[Any, bool]):
113
124
  """Only show items when ``values == True`` in ``__repr__()``"""
114
125
 
@@ -125,6 +136,18 @@ class DictBoolFalse(Dict[Any, bool]):
125
136
  return _dict_bool(temp, False).__repr__()
126
137
 
127
138
 
139
+ # class DictNoDunder(dict): # W.I.P
140
+ # """Remove dunder methods in ``__repr__()`` of dict"""
141
+
142
+ # def __repr__(self) -> str:
143
+ # temp = self.copy()
144
+ # out = dict()
145
+ # for k, v in temp.items():
146
+ # if not str(k).startswith("__"):
147
+ # out.__setattr__(k, v)
148
+ # return out.__repr__()
149
+
150
+
128
151
  # Class
129
152
  ###########################################################################
130
153
  class Text(str):
@@ -419,6 +442,48 @@ class Text(str):
419
442
  logger.debug(temp)
420
443
  return __class__("".join(temp))
421
444
 
445
+ def reverse_capslock(self) -> "Text":
446
+ """
447
+ Reverse capslock in string
448
+
449
+ Returns
450
+ -------
451
+ Text
452
+ Reversed capslock ``Text``
453
+
454
+
455
+ Example:
456
+ --------
457
+ >>> test = Text("Foo")
458
+ >>> test.reverse_capslock()
459
+ 'fOO'
460
+ """
461
+ temp = list(self)
462
+ for i, x in enumerate(temp):
463
+ if x.isupper():
464
+ temp[i] = x.lower()
465
+ else:
466
+ temp[i] = x.upper()
467
+ return __class__("".join(temp))
468
+
469
+ def to_list(self) -> List[str]:
470
+ """
471
+ Convert into list
472
+
473
+ Returns
474
+ -------
475
+ list[str]
476
+ List of string
477
+
478
+
479
+ Example:
480
+ --------
481
+ >>> test = Text("test")
482
+ >>> test.to_list()
483
+ ['t', 'e', 's', 't']
484
+ """
485
+ return list(self)
486
+
422
487
 
423
488
  class IntNumber(int):
424
489
  """
@@ -776,7 +841,10 @@ class IntNumber(int):
776
841
  >>> test.gcd(8)
777
842
  8
778
843
  """
779
- return __class__(math.gcd(self, with_number))
844
+ if python_version.micro < 9:
845
+ return __class__(math.gcd(self, with_number))
846
+ else:
847
+ return __class__(math.lcm(self, with_number))
780
848
 
781
849
  def add_to_one_digit(self, master_number: bool = False) -> "IntNumber":
782
850
  """
@@ -806,7 +874,7 @@ class IntNumber(int):
806
874
  11
807
875
  """
808
876
  number = self
809
- if number <= 1:
877
+ if number < 0:
810
878
  number *= -1
811
879
  logger.debug(f"Current number: {number}")
812
880
  while len(str(number)) != 1:
@@ -847,7 +915,8 @@ class IntNumber(int):
847
915
  divi_list = [x for x in range(1, int(self / 2) + 1) if self % x == 0] + [self]
848
916
 
849
917
  if short_form:
850
- return ListREPR(divi_list)
918
+ return divi_list
919
+ # return ListREPR(divi_list) ## FIX LATER
851
920
  return divi_list
852
921
 
853
922
  def prime_factor(self, short_form: bool = True) -> Union[List[int], List[Pow]]:
@@ -921,8 +990,8 @@ class IntNumber(int):
921
990
  >>> test = IntNumber(1024)
922
991
  >>> test.analyze()
923
992
  {
924
- 'summary': {'number': 1024, 'length': 4, 'even': True, 'prime factor': [2^10], 'divisible': [1, 2, 4, 8, ...,128, 256, 512, 1024] [Len: 11]},
925
- 'convert': {'binary': '10000000000', 'reverse': 4201, 'add to one': 7},
993
+ 'summary': {'number': 1024, 'length': 4, 'even': True, 'prime factor': [2^10], 'divisible': [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]},
994
+ 'convert': {'binary': '10000000000', 'octa': '2000', 'hex': '400', 'reverse': 4201, 'add to one': 7},
926
995
  'characteristic': {'prime': False, 'twisted prime': False, 'perfect': False, 'narcissistic': False, 'palindromic': False, 'palindromic prime': False}
927
996
  }
928
997
  """
@@ -935,7 +1004,10 @@ class IntNumber(int):
935
1004
  "divisible": self.divisible_list(short_form=short_form),
936
1005
  },
937
1006
  "convert": {
938
- "binary": self.to_binary(),
1007
+ "binary": bin(self)[2:],
1008
+ "octa": oct(self)[2:],
1009
+ "hex": hex(self)[2:],
1010
+ # "hash": hash(self),
939
1011
  "reverse": self.reverse(),
940
1012
  "add to one": self.add_to_one_digit(),
941
1013
  },
@@ -1235,7 +1307,7 @@ class ListExt(list):
1235
1307
  logger.debug(out)
1236
1308
  return out
1237
1309
 
1238
- def apply(self, func) -> "ListExt":
1310
+ def apply(self, func: Callable) -> "ListExt":
1239
1311
  """
1240
1312
  Apply function to each entry
1241
1313
 
@@ -1301,6 +1373,13 @@ class ListExt(list):
1301
1373
  temp = groupby(self.sorts())
1302
1374
  return __class__([list(g) for _, g in temp])
1303
1375
 
1376
+ @staticmethod
1377
+ def _group_by_unique(iterable: list) -> List[list]:
1378
+ """
1379
+ Static method for ``group_by_unique``
1380
+ """
1381
+ return list([list(g) for _, g in groupby(iterable)])
1382
+
1304
1383
  def group_by_pair_value(self, max_loop: int = 3) -> List[list]:
1305
1384
  """
1306
1385
  Assume each ``list`` in ``list`` is a pair value,
@@ -1353,6 +1432,62 @@ class ListExt(list):
1353
1432
 
1354
1433
  return list(x for x, _ in groupby(iter))
1355
1434
 
1435
+ def flatten(self) -> "ListExt":
1436
+ """
1437
+ Flatten the list
1438
+
1439
+ Returns
1440
+ -------
1441
+ ListExt
1442
+ Flattened list
1443
+
1444
+
1445
+ Example:
1446
+ --------
1447
+ >>> test = ListExt([["test"], ["test", "test"], ["test"]])
1448
+ >>> test.flatten()
1449
+ ['test', 'test', 'test', 'test']
1450
+ """
1451
+ try:
1452
+ return ListExt(chain(*self))
1453
+ except:
1454
+ temp = list(map(lambda x: x if isinstance(x, list) else [x], self))
1455
+ return ListExt(chain(*temp))
1456
+
1457
+ def numbering(self, start: int = 0) -> "ListExt":
1458
+ """
1459
+ Number the item in list
1460
+ (``enumerate`` wrapper)
1461
+
1462
+ Parameters
1463
+ ----------
1464
+ start : int
1465
+ Start from which number
1466
+ (Default: ``0``)
1467
+
1468
+ Returns
1469
+ -------
1470
+ ListExt
1471
+ Counted list
1472
+
1473
+
1474
+ Example:
1475
+ --------
1476
+ >>> test = ListExt([9, 9, 9])
1477
+ >>> test.numbering()
1478
+ [(0, 9), (1, 9), (2, 9)]
1479
+ """
1480
+ start = set_min(start, min_value=0)
1481
+ return ListExt(enumerate(self, start=start))
1482
+
1483
+ @staticmethod
1484
+ def _numbering(iterable: list, start: int = 0) -> List[Tuple[int, Any]]:
1485
+ """
1486
+ Static method for ``numbering``
1487
+ """
1488
+ start = set_min(start, min_value=0)
1489
+ return list(enumerate(iterable, start=start))
1490
+
1356
1491
 
1357
1492
  class DictExt(dict):
1358
1493
  """
@@ -1434,7 +1569,7 @@ class DictExt(dict):
1434
1569
  """
1435
1570
  return __class__(zip(self.values(), self.keys()))
1436
1571
 
1437
- def apply(self, func, apply_to_value: bool = True) -> "DictExt":
1572
+ def apply(self, func: Callable, apply_to_value: bool = True) -> "DictExt":
1438
1573
  """
1439
1574
  Apply function to ``DictExt.keys()`` or ``DictExt.values()``
1440
1575
 
@@ -1472,3 +1607,4 @@ class DictExt(dict):
1472
1607
  ###########################################################################
1473
1608
  if __name__ == "__main__":
1474
1609
  logger.setLevel(10)
1610
+ # from rich import print