absfuyu 3.1.1__py3-none-any.whl → 3.3.3__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/__init__.py +3 -10
- absfuyu/__main__.py +5 -250
- absfuyu/cli/__init__.py +51 -0
- absfuyu/cli/color.py +24 -0
- absfuyu/cli/config_group.py +56 -0
- absfuyu/cli/do_group.py +76 -0
- absfuyu/cli/game_group.py +109 -0
- absfuyu/config/__init__.py +117 -100
- absfuyu/config/config.json +0 -7
- absfuyu/core.py +5 -66
- absfuyu/everything.py +7 -9
- absfuyu/extensions/beautiful.py +30 -23
- absfuyu/extensions/dev/__init__.py +11 -8
- absfuyu/extensions/dev/password_hash.py +4 -2
- absfuyu/extensions/dev/passwordlib.py +7 -5
- absfuyu/extensions/dev/project_starter.py +4 -2
- absfuyu/extensions/dev/shutdownizer.py +148 -0
- absfuyu/extensions/extra/__init__.py +1 -2
- absfuyu/extensions/extra/data_analysis.py +182 -107
- absfuyu/fun/WGS.py +50 -26
- absfuyu/fun/__init__.py +6 -7
- absfuyu/fun/tarot.py +1 -1
- absfuyu/game/__init__.py +75 -81
- absfuyu/game/game_stat.py +36 -0
- absfuyu/game/sudoku.py +41 -48
- absfuyu/game/tictactoe.py +303 -548
- absfuyu/game/wordle.py +56 -47
- absfuyu/general/__init__.py +17 -7
- absfuyu/general/content.py +16 -15
- absfuyu/general/data_extension.py +282 -90
- absfuyu/general/generator.py +67 -67
- absfuyu/general/human.py +74 -78
- absfuyu/logger.py +94 -68
- absfuyu/pkg_data/__init__.py +29 -25
- absfuyu/py.typed +0 -0
- absfuyu/sort.py +61 -47
- absfuyu/tools/__init__.py +0 -1
- absfuyu/tools/converter.py +80 -62
- absfuyu/tools/keygen.py +62 -67
- absfuyu/tools/obfuscator.py +57 -53
- absfuyu/tools/stats.py +24 -24
- absfuyu/tools/web.py +10 -9
- absfuyu/util/__init__.py +71 -33
- absfuyu/util/api.py +53 -43
- absfuyu/util/json_method.py +25 -27
- absfuyu/util/lunar.py +20 -24
- absfuyu/util/path.py +362 -241
- absfuyu/util/performance.py +217 -135
- absfuyu/util/pkl.py +8 -8
- absfuyu/util/zipped.py +17 -19
- absfuyu/version.py +160 -147
- absfuyu-3.3.3.dist-info/METADATA +124 -0
- absfuyu-3.3.3.dist-info/RECORD +59 -0
- {absfuyu-3.1.1.dist-info → absfuyu-3.3.3.dist-info}/WHEEL +1 -2
- {absfuyu-3.1.1.dist-info → absfuyu-3.3.3.dist-info}/entry_points.txt +1 -0
- {absfuyu-3.1.1.dist-info → absfuyu-3.3.3.dist-info/licenses}/LICENSE +1 -1
- absfuyu/extensions/dev/pkglib.py +0 -98
- absfuyu/game/tictactoe2.py +0 -318
- absfuyu-3.1.1.dist-info/METADATA +0 -215
- absfuyu-3.1.1.dist-info/RECORD +0 -55
- absfuyu-3.1.1.dist-info/top_level.txt +0 -1
absfuyu/fun/__init__.py
CHANGED
|
@@ -3,16 +3,15 @@ Absfuyu: Fun
|
|
|
3
3
|
------------
|
|
4
4
|
Some fun or weird stuff
|
|
5
5
|
|
|
6
|
-
Version: 1.0.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.0.6
|
|
7
|
+
Date updated: 05/04/2023 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
# Library
|
|
12
11
|
###########################################################################
|
|
13
|
-
from datetime import date
|
|
14
12
|
import subprocess
|
|
15
13
|
import sys
|
|
14
|
+
from datetime import date
|
|
16
15
|
|
|
17
16
|
from absfuyu.logger import logger
|
|
18
17
|
from absfuyu.util.api import APIRequest
|
|
@@ -55,7 +54,7 @@ def zodiac_sign(
|
|
|
55
54
|
0 < month < 13,
|
|
56
55
|
]
|
|
57
56
|
if not all(conditions):
|
|
58
|
-
raise
|
|
57
|
+
raise ValueError("Value out of range")
|
|
59
58
|
|
|
60
59
|
zodiac = {
|
|
61
60
|
"Aquarius": any(
|
|
@@ -148,8 +147,8 @@ def im_bored() -> str:
|
|
|
148
147
|
"""
|
|
149
148
|
try:
|
|
150
149
|
api = APIRequest("https://www.boredapi.com/api/activity")
|
|
151
|
-
return api.fetch_data_only().json()["activity"]
|
|
152
|
-
except:
|
|
150
|
+
return api.fetch_data_only().json()["activity"] # type: ignore
|
|
151
|
+
except Exception:
|
|
153
152
|
return "FAILED"
|
|
154
153
|
|
|
155
154
|
|
absfuyu/fun/tarot.py
CHANGED
absfuyu/game/__init__.py
CHANGED
|
@@ -3,65 +3,66 @@ Absfuyu: Game
|
|
|
3
3
|
-------------
|
|
4
4
|
Contain some game that can be played on terminal
|
|
5
5
|
|
|
6
|
-
Version: 1.0
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.1.0
|
|
7
|
+
Date updated: 14/04/2024 (mm/dd/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
# Module Package
|
|
12
|
-
###########################################################################
|
|
13
10
|
__all__ = [
|
|
14
|
-
"game_escapeLoop",
|
|
11
|
+
"game_escapeLoop",
|
|
12
|
+
"game_RockPaperScissors",
|
|
15
13
|
]
|
|
16
14
|
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
import random
|
|
17
|
+
import time
|
|
18
|
+
|
|
19
|
+
from .game_stat import GameStats
|
|
20
|
+
|
|
21
|
+
# Escape loop
|
|
22
|
+
_ESCAPE_LOOP_GAME_MSG = """\
|
|
23
|
+
Are you sure about this?
|
|
24
|
+
Don't leave me =((
|
|
25
|
+
I can't believe you did this to me!
|
|
26
|
+
Are you very much sure?
|
|
27
|
+
I'll be sad. Pick again please.
|
|
28
|
+
I still don't believe you.
|
|
29
|
+
Choose again.
|
|
30
|
+
You actually have to answer the correct keyword
|
|
31
|
+
I think you need to choose again.
|
|
32
|
+
Last chance!
|
|
33
|
+
Okay okay, i believe you ;)
|
|
34
|
+
Almost there.
|
|
35
|
+
I can do this all day
|
|
36
|
+
So close!
|
|
37
|
+
You can't escape from me.
|
|
38
|
+
How are you still here, just to suffer?
|
|
39
|
+
Never gonna give you up
|
|
40
|
+
Never gonna let you down
|
|
41
|
+
"""
|
|
21
42
|
|
|
22
43
|
|
|
23
|
-
# Default games
|
|
24
|
-
###########################################################################
|
|
25
44
|
def game_escapeLoop() -> None:
|
|
26
45
|
"""Try to escape the infinite loop"""
|
|
27
|
-
|
|
46
|
+
|
|
28
47
|
init = True
|
|
29
48
|
welcome_messages = [
|
|
30
49
|
"Congrats! You are now stuck inside an infinite loop.",
|
|
31
|
-
"Do you want to escape this loop?"
|
|
50
|
+
"Do you want to escape this loop?",
|
|
32
51
|
]
|
|
33
52
|
|
|
34
|
-
num1 =
|
|
35
|
-
num2 =
|
|
53
|
+
num1 = random.choice(range(2, 13))
|
|
54
|
+
num2 = random.choice(range(2, 13))
|
|
36
55
|
hidden_answer = str(num1 * num2)
|
|
37
56
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"Don't leave me =((",
|
|
41
|
-
"I can't believe you did this to me!",
|
|
42
|
-
"Are you very much sure?",
|
|
43
|
-
"I'll be sad. Pick again please.",
|
|
44
|
-
"I still don't believe you.",
|
|
45
|
-
"Choose again.",
|
|
46
|
-
"You actually have to answer the correct keyword",
|
|
47
|
-
"I think you need to choose again.",
|
|
48
|
-
"Last chance!",
|
|
49
|
-
"Okay okay, i believe you ;)",
|
|
50
|
-
"Almost there.",
|
|
51
|
-
"I can do this all day",
|
|
52
|
-
"So close!",
|
|
53
|
-
"You can't escape from me.",
|
|
54
|
-
"How are you still here, just to suffer?",
|
|
55
|
-
"Never gonna give you up",
|
|
56
|
-
"Never gonna let you down",
|
|
57
|
+
gm_msg = {x for x in _ESCAPE_LOOP_GAME_MSG.splitlines() if len(x) > 0}
|
|
58
|
+
game_messages = list(gm_msg) + [
|
|
57
59
|
f"Hint 01: The keyword is: {num1}",
|
|
58
|
-
f"Hint 02:
|
|
60
|
+
f"Hint 02: {num2}",
|
|
59
61
|
]
|
|
60
62
|
|
|
61
|
-
congrats_messages = [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
congrats_messages = ["Congratulation! You've escaped."]
|
|
64
|
+
|
|
65
|
+
start_time = time.time()
|
|
65
66
|
while True:
|
|
66
67
|
# Welcome
|
|
67
68
|
if init:
|
|
@@ -69,9 +70,9 @@ def game_escapeLoop() -> None:
|
|
|
69
70
|
print(x)
|
|
70
71
|
answer = str(input())
|
|
71
72
|
init = False
|
|
72
|
-
|
|
73
|
+
|
|
73
74
|
# Random text
|
|
74
|
-
mess =
|
|
75
|
+
mess = random.choice(game_messages)
|
|
75
76
|
print(mess)
|
|
76
77
|
|
|
77
78
|
# Condition check
|
|
@@ -79,24 +80,22 @@ def game_escapeLoop() -> None:
|
|
|
79
80
|
if answer == hidden_answer:
|
|
80
81
|
for x in congrats_messages:
|
|
81
82
|
print(x)
|
|
83
|
+
stop_time = time.time()
|
|
82
84
|
break
|
|
83
|
-
|
|
85
|
+
print(f"= Escaped in {stop_time-start_time:,.2f}s =")
|
|
84
86
|
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
# Rock Paper Scissors
|
|
89
|
+
def game_RockPaperScissors(hard_mode: bool = False) -> GameStats:
|
|
87
90
|
"""
|
|
88
91
|
Rock Paper Scissors
|
|
89
|
-
|
|
92
|
+
|
|
90
93
|
:param hard_mode: The bot only win or draw (Default: ``False``)
|
|
91
94
|
:type hard_mode: bool
|
|
92
95
|
"""
|
|
93
|
-
|
|
94
|
-
state_dict = {
|
|
95
|
-
|
|
96
|
-
1: "paper",
|
|
97
|
-
2: "scissors"
|
|
98
|
-
}
|
|
99
|
-
|
|
96
|
+
|
|
97
|
+
state_dict = {"0": "rock", "1": "paper", "2": "scissors"}
|
|
98
|
+
|
|
100
99
|
init = True
|
|
101
100
|
|
|
102
101
|
end_message = "end"
|
|
@@ -108,67 +107,62 @@ def game_RockPaperScissors(hard_mode=False):
|
|
|
108
107
|
|
|
109
108
|
game_messages = [
|
|
110
109
|
"Pick one option to begin:",
|
|
110
|
+
" ".join([f"{k}={v}" for k, v in state_dict.items()]),
|
|
111
111
|
]
|
|
112
112
|
|
|
113
|
-
game_summary =
|
|
114
|
-
|
|
115
|
-
"Draw": 0,
|
|
116
|
-
"Lose": 0
|
|
117
|
-
}
|
|
118
|
-
|
|
113
|
+
game_summary = GameStats()
|
|
114
|
+
|
|
119
115
|
while True:
|
|
120
116
|
# Welcome
|
|
121
117
|
if init:
|
|
122
118
|
for x in welcome_messages:
|
|
123
119
|
print(x)
|
|
124
120
|
init = False
|
|
125
|
-
|
|
121
|
+
|
|
126
122
|
# Game start
|
|
127
123
|
print("")
|
|
128
124
|
for x in game_messages:
|
|
129
125
|
print(x)
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
|
|
132
127
|
# Player's choice
|
|
133
|
-
answer = input()
|
|
128
|
+
answer = input().strip().lower()
|
|
134
129
|
|
|
135
130
|
# Condition check
|
|
136
131
|
if answer == end_message:
|
|
137
132
|
print(game_summary)
|
|
138
133
|
break
|
|
139
|
-
|
|
140
|
-
elif answer not in ["0","1","2"]:
|
|
134
|
+
|
|
135
|
+
elif answer not in ["0", "1", "2"]:
|
|
141
136
|
print("Invalid option. Choose again!")
|
|
142
|
-
|
|
137
|
+
|
|
143
138
|
else:
|
|
144
139
|
# Calculation
|
|
145
|
-
answer = int(answer)
|
|
146
140
|
if hard_mode:
|
|
147
|
-
if answer == 0:
|
|
148
|
-
game_choice =
|
|
149
|
-
if answer == 1:
|
|
150
|
-
game_choice =
|
|
151
|
-
if answer == 2:
|
|
152
|
-
game_choice =
|
|
141
|
+
if answer == "0":
|
|
142
|
+
game_choice = random.choice(["0", "1"])
|
|
143
|
+
if answer == "1":
|
|
144
|
+
game_choice = random.choice(["1", "2"])
|
|
145
|
+
if answer == "2":
|
|
146
|
+
game_choice = random.choice(["0", "2"])
|
|
153
147
|
else:
|
|
154
|
-
game_choice =
|
|
148
|
+
game_choice = random.choice(["0", "1", "2"])
|
|
155
149
|
print(f"You picked: {state_dict[answer]}")
|
|
156
150
|
print(f"BOT picked: {state_dict[game_choice]}")
|
|
157
|
-
|
|
158
|
-
if answer == 2 and game_choice == 0:
|
|
151
|
+
|
|
152
|
+
if answer == "2" and game_choice == "0":
|
|
159
153
|
print("You Lose!")
|
|
160
|
-
game_summary
|
|
161
|
-
elif answer == 0 and game_choice == 2:
|
|
154
|
+
game_summary.update_score("lose")
|
|
155
|
+
elif answer == "0" and game_choice == "2":
|
|
162
156
|
print("You Win!")
|
|
163
|
-
game_summary
|
|
157
|
+
game_summary.update_score("win")
|
|
164
158
|
elif answer == game_choice:
|
|
165
159
|
print("Draw Match!")
|
|
166
|
-
game_summary
|
|
160
|
+
game_summary.update_score("draw")
|
|
167
161
|
elif answer > game_choice:
|
|
168
162
|
print("You Win!")
|
|
169
|
-
game_summary
|
|
163
|
+
game_summary.update_score("win")
|
|
170
164
|
else:
|
|
171
165
|
print("You Lose!")
|
|
172
|
-
game_summary
|
|
166
|
+
game_summary.update_score("lose")
|
|
173
167
|
|
|
174
|
-
return game_summary
|
|
168
|
+
return game_summary
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Game: Game Stat
|
|
3
|
+
|
|
4
|
+
Version: 1.0.0
|
|
5
|
+
Date updated: 14/04/2024 (dd/mm/yyyy)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__all__ = ["GameStats"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Literal
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class GameStats:
|
|
17
|
+
win: int = field(default=0)
|
|
18
|
+
draw: int = field(default=0)
|
|
19
|
+
lose: int = field(default=0)
|
|
20
|
+
win_rate: str = field(init=False)
|
|
21
|
+
_win_rate: float = field(init=False, repr=False)
|
|
22
|
+
|
|
23
|
+
def __post_init__(self) -> None:
|
|
24
|
+
self._update_win_rate()
|
|
25
|
+
|
|
26
|
+
def _update_win_rate(self) -> None:
|
|
27
|
+
try:
|
|
28
|
+
self._win_rate = self.win / (self.win + self.draw + self.lose)
|
|
29
|
+
self.win_rate = f"{self._win_rate*100:,.2f}%"
|
|
30
|
+
except ZeroDivisionError:
|
|
31
|
+
self._win_rate = 0
|
|
32
|
+
self.win_rate = "N/A"
|
|
33
|
+
|
|
34
|
+
def update_score(self, option: Literal["win", "draw", "lose"]) -> None:
|
|
35
|
+
self.__setattr__(option, self.__getattribute__(option) + 1)
|
|
36
|
+
self._update_win_rate()
|
absfuyu/game/sudoku.py
CHANGED
|
@@ -12,21 +12,12 @@ Credit:
|
|
|
12
12
|
- [Solve algo](https://www.techwithtim.net/tutorials/python-programming/sudoku-solver-backtracking/)
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
# Module level
|
|
17
|
-
###########################################################################
|
|
18
|
-
__all__ = [
|
|
19
|
-
"Sudoku"
|
|
20
|
-
]
|
|
15
|
+
__all__ = ["Sudoku"]
|
|
21
16
|
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
###########################################################################
|
|
25
|
-
from typing import List
|
|
18
|
+
from typing import List, Literal, Tuple
|
|
26
19
|
|
|
27
20
|
|
|
28
|
-
# Class
|
|
29
|
-
###########################################################################
|
|
30
21
|
class Sudoku:
|
|
31
22
|
def __init__(self, sudoku_data: List[List[int]]) -> None:
|
|
32
23
|
self.data = sudoku_data
|
|
@@ -34,21 +25,23 @@ class Sudoku:
|
|
|
34
25
|
self._row_len = len(self.data)
|
|
35
26
|
self._col_len = len(self.data[0])
|
|
36
27
|
# self.solved = None
|
|
28
|
+
|
|
37
29
|
def __str__(self) -> str:
|
|
38
30
|
return f"{self.__class__.__name__}({self.export_to_string()})"
|
|
31
|
+
|
|
39
32
|
def __repr__(self) -> str:
|
|
40
33
|
return self.__str__()
|
|
41
|
-
|
|
42
|
-
def export_to_string(self, *, style:
|
|
34
|
+
|
|
35
|
+
def export_to_string(self, *, style: Literal["dot", "zero"] = "dot") -> str:
|
|
43
36
|
"""
|
|
44
37
|
Export Sudoku data to string form
|
|
45
38
|
|
|
46
39
|
Parameters
|
|
47
40
|
----------
|
|
48
|
-
style :
|
|
41
|
+
style : Literal["dot", "zero"]
|
|
49
42
|
- "zero": ``0`` is ``0``
|
|
50
43
|
- "dot": ``0`` is ``.``
|
|
51
|
-
|
|
44
|
+
|
|
52
45
|
Returns
|
|
53
46
|
-------
|
|
54
47
|
str
|
|
@@ -57,16 +50,16 @@ class Sudoku:
|
|
|
57
50
|
style_option = ["zero", "dot"]
|
|
58
51
|
if style.lower() not in style_option:
|
|
59
52
|
style = "dot"
|
|
60
|
-
|
|
53
|
+
|
|
61
54
|
out = "".join(str(self.data))
|
|
62
55
|
remove = ["[", "]", " ", ","]
|
|
63
56
|
for x in remove:
|
|
64
57
|
out = out.replace(x, "")
|
|
65
|
-
|
|
58
|
+
|
|
66
59
|
if style.startswith("zero"):
|
|
67
60
|
return out
|
|
68
61
|
elif style.startswith("dot"):
|
|
69
|
-
out = out.replace("0",".")
|
|
62
|
+
out = out.replace("0", ".")
|
|
70
63
|
return out
|
|
71
64
|
else:
|
|
72
65
|
return out
|
|
@@ -80,7 +73,7 @@ class Sudoku:
|
|
|
80
73
|
----------
|
|
81
74
|
string_data : str
|
|
82
75
|
String data
|
|
83
|
-
|
|
76
|
+
|
|
84
77
|
Returns
|
|
85
78
|
-------
|
|
86
79
|
Sudoku
|
|
@@ -102,38 +95,39 @@ class Sudoku:
|
|
|
102
95
|
"""
|
|
103
96
|
if len(string_data) == 81:
|
|
104
97
|
# Convert
|
|
105
|
-
sdk = str(string_data).replace(".","0")
|
|
106
|
-
|
|
98
|
+
sdk = str(string_data).replace(".", "0")
|
|
99
|
+
|
|
107
100
|
# Divide into 9 equal parts
|
|
108
101
|
temp = []
|
|
109
102
|
while len(sdk) != 0:
|
|
110
103
|
temp.append(sdk[:9])
|
|
111
104
|
sdk = sdk[9:]
|
|
112
|
-
|
|
105
|
+
|
|
113
106
|
# Turn into list[list[int]]
|
|
114
107
|
out = []
|
|
115
108
|
for value in temp:
|
|
116
109
|
temp_list = [int(x) for x in value]
|
|
117
110
|
out.append(temp_list)
|
|
118
|
-
|
|
111
|
+
|
|
119
112
|
else:
|
|
120
113
|
# Invalid length
|
|
121
114
|
raise ValueError("Invalid length")
|
|
122
115
|
return cls(out)
|
|
123
|
-
|
|
116
|
+
|
|
124
117
|
@classmethod
|
|
125
118
|
def hardest_sudoku(cls):
|
|
126
119
|
"""
|
|
127
|
-
Create Hardest Sudoku instance
|
|
120
|
+
Create Hardest Sudoku instance
|
|
128
121
|
([Source](https://www.telegraph.co.uk/news/science/science-news/9359579/Worlds-hardest-sudoku-can-you-crack-it.html))
|
|
129
|
-
|
|
122
|
+
|
|
130
123
|
Returns
|
|
131
124
|
-------
|
|
132
125
|
Sudoku
|
|
133
126
|
``Sudoku`` instance
|
|
134
127
|
"""
|
|
135
|
-
return cls.from_string(
|
|
136
|
-
|
|
128
|
+
return cls.from_string(
|
|
129
|
+
"8..........36......7..9.2...5...7.......457.....1...3...1....68..85...1..9....4.."
|
|
130
|
+
)
|
|
137
131
|
|
|
138
132
|
def to_board_form(self) -> str:
|
|
139
133
|
"""
|
|
@@ -144,7 +138,7 @@ class Sudoku:
|
|
|
144
138
|
str
|
|
145
139
|
Sudoku Board data that ready to print
|
|
146
140
|
|
|
147
|
-
|
|
141
|
+
|
|
148
142
|
Example:
|
|
149
143
|
--------
|
|
150
144
|
>>> demo = Sudoku.hardest_sudoku()
|
|
@@ -167,22 +161,21 @@ class Sudoku:
|
|
|
167
161
|
for row in range(self._row_len):
|
|
168
162
|
if row % 3 == 0:
|
|
169
163
|
if row == 0:
|
|
170
|
-
out_string = out_string +
|
|
164
|
+
out_string = out_string + " \u250E" + "\u2500" * 29 + "\u2512\n"
|
|
171
165
|
else:
|
|
172
|
-
out_string = out_string +
|
|
166
|
+
out_string = out_string + " \u2520" + "\u2500" * 29 + "\u2528\n"
|
|
173
167
|
|
|
174
168
|
for col in range(self._col_len):
|
|
175
169
|
if col % 3 == 0:
|
|
176
|
-
out_string +=
|
|
170
|
+
out_string += " \u2503 "
|
|
177
171
|
|
|
178
172
|
if col == 8:
|
|
179
|
-
out_string = out_string + str(self.data[row][col]) +
|
|
173
|
+
out_string = out_string + str(self.data[row][col]) + " \u2503\n"
|
|
180
174
|
else:
|
|
181
175
|
out_string = out_string + str(self.data[row][col]) + " "
|
|
182
176
|
|
|
183
|
-
out_string = out_string +
|
|
177
|
+
out_string = out_string + " \u2516" + "\u2500" * 29 + "\u251A"
|
|
184
178
|
return out_string
|
|
185
|
-
|
|
186
179
|
|
|
187
180
|
# Solve
|
|
188
181
|
def _find_empty(self):
|
|
@@ -200,15 +193,15 @@ class Sudoku:
|
|
|
200
193
|
return (row, col)
|
|
201
194
|
# Return None when there is no empty cell
|
|
202
195
|
return None
|
|
203
|
-
|
|
204
|
-
def _is_valid(self, number: int, position:
|
|
196
|
+
|
|
197
|
+
def _is_valid(self, number: int, position: Tuple[int, int]) -> bool:
|
|
205
198
|
"""
|
|
206
199
|
Check valid number value in row, column, box
|
|
207
200
|
"""
|
|
208
|
-
row, col = position
|
|
201
|
+
row, col = position # unpack tuple
|
|
209
202
|
|
|
210
203
|
# Check row
|
|
211
|
-
for i in range(self._col_len):
|
|
204
|
+
for i in range(self._col_len): # each item in row i; row i has `col_len` items
|
|
212
205
|
if self.data[row][i] == number and col != i:
|
|
213
206
|
return False
|
|
214
207
|
|
|
@@ -236,12 +229,12 @@ class Sudoku:
|
|
|
236
229
|
# Find empty cell
|
|
237
230
|
empty_pos = self._find_empty()
|
|
238
231
|
if empty_pos is None:
|
|
239
|
-
return True
|
|
232
|
+
return True # solve_state (True: every cell filled)
|
|
240
233
|
else:
|
|
241
234
|
# unpack position when exist empty cell
|
|
242
235
|
row, col = empty_pos
|
|
243
236
|
|
|
244
|
-
for num in range(1,10):
|
|
237
|
+
for num in range(1, 10): # sudoku value (1-9)
|
|
245
238
|
if self._is_valid(num, empty_pos):
|
|
246
239
|
self.data[row][col] = num
|
|
247
240
|
|
|
@@ -253,17 +246,17 @@ class Sudoku:
|
|
|
253
246
|
|
|
254
247
|
# End recursive
|
|
255
248
|
return False
|
|
256
|
-
|
|
249
|
+
|
|
257
250
|
def solve(self):
|
|
258
251
|
"""
|
|
259
252
|
Solve the Sudoku
|
|
260
|
-
|
|
253
|
+
|
|
261
254
|
Returns
|
|
262
255
|
-------
|
|
263
256
|
Sudoku
|
|
264
257
|
``Sudoku`` instance
|
|
265
|
-
|
|
266
|
-
|
|
258
|
+
|
|
259
|
+
|
|
267
260
|
Example:
|
|
268
261
|
--------
|
|
269
262
|
>>> test = Sudoku.hardest_sudoku()
|
|
@@ -286,11 +279,11 @@ class Sudoku:
|
|
|
286
279
|
self._solve()
|
|
287
280
|
# self.solved = self.data
|
|
288
281
|
# self.data = self._original
|
|
289
|
-
return __class__(self.data)
|
|
290
|
-
|
|
282
|
+
return self.__class__(self.data)
|
|
283
|
+
|
|
291
284
|
|
|
292
285
|
# Run
|
|
293
286
|
###########################################################################
|
|
294
287
|
if __name__ == "__main__":
|
|
295
288
|
test = Sudoku.hardest_sudoku()
|
|
296
|
-
print(test.solve().to_board_form())
|
|
289
|
+
print(test.solve().to_board_form())
|