lercube 0.1.0__tar.gz

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.
lercube-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: lercube
3
+ Version: 0.1.0
4
+ Summary: Rubik's cube simulator and solver
5
+ Requires-Python: >=3.10
@@ -0,0 +1,23 @@
1
+ from .cube import Cube
2
+ from .dict import (
3
+ CORNER_COLOURS_TO_LETTERS,
4
+ CORNER_LETTERS,
5
+ CORNER_LETTER_PLACEMENTS,
6
+ CORNER_LETTER_PLACEMENTS_FLIPPED,
7
+ EDGE_COLOURS_TO_LETTERS,
8
+ EDGE_LETTERS,
9
+ EDGE_LETTER_PLACEMENTS,
10
+ EDGE_LETTER_PLACEMENTS_FLIPPED,
11
+ )
12
+
13
+ __all__ = [
14
+ "Cube",
15
+ "CORNER_COLOURS_TO_LETTERS",
16
+ "CORNER_LETTERS",
17
+ "CORNER_LETTER_PLACEMENTS",
18
+ "CORNER_LETTER_PLACEMENTS_FLIPPED",
19
+ "EDGE_COLOURS_TO_LETTERS",
20
+ "EDGE_LETTERS",
21
+ "EDGE_LETTER_PLACEMENTS",
22
+ "EDGE_LETTER_PLACEMENTS_FLIPPED",
23
+ ]
lercube-0.1.0/cube.py ADDED
@@ -0,0 +1,316 @@
1
+ from .dict import *
2
+ import random
3
+
4
+ POSSIBLE_MOVES_FULL = [f"{x}{y}" for x in "FBRLUDfbrludxys" for y in ["", "'", "2"]]
5
+ POSSIBLE_MOVES_MAIN = [f"{x}{y}" for x in "FBRLUD" for y in ["", "'", "2"]]
6
+
7
+ TRANSLATE_MOVE = {
8
+ move: (
9
+ str(move[0]),
10
+ ["", "2", "'"].index(move[1:]) + 1,
11
+ )
12
+ for move in POSSIBLE_MOVES_FULL
13
+ }
14
+
15
+ class Cube:
16
+ def __init__(self):
17
+ self.cube = {
18
+ face: [[col] * 3 for _ in range(3)]
19
+ for face, col in zip("ULFRBD", 'WOGRBY')
20
+ }
21
+
22
+ @staticmethod
23
+ def generate_scramble(moveCount: int = 20, xyz: bool = False) -> list[str]:
24
+ possible_moves = POSSIBLE_MOVES_FULL if xyz else POSSIBLE_MOVES_MAIN
25
+ scramble = []
26
+ previous_face = None
27
+
28
+ while len(scramble) < moveCount:
29
+ move = random.choice(possible_moves)
30
+ face = move[0].lower()
31
+
32
+ if face == previous_face:
33
+ continue
34
+
35
+ scramble.append(move)
36
+ previous_face = face
37
+
38
+ return scramble
39
+
40
+ def translate(self, moves):
41
+ if moves is None:
42
+ return
43
+
44
+ if type(moves) is str:
45
+ moves = moves.split()
46
+
47
+ for move in moves:
48
+ if move not in POSSIBLE_MOVES_FULL:
49
+ raise ValueError(f"Move {move} is not a valid move.")
50
+
51
+ method_name, count = TRANSLATE_MOVE[move]
52
+ for _ in range(count):
53
+ getattr(self, method_name)()
54
+
55
+ #region face rotation helpers
56
+ def _rotate_face(self, face):
57
+ return [list(row) for row in zip(*face[::-1])]
58
+ def _rotate_face_twice(self, face):
59
+ return self._rotate_face(self._rotate_face(face))
60
+ def _rotate_face_counterclockwise(self, face):
61
+ return self._rotate_face(self._rotate_face(self._rotate_face(face)))
62
+ #endregion
63
+
64
+ #region moves
65
+ def F(self):
66
+ u_bottom = self.cube["U"][2].copy()
67
+ r_left = [row[0] for row in self.cube["R"]]
68
+ d_top = self.cube["D"][0].copy()
69
+ l_right = [row[2] for row in self.cube["L"]]
70
+
71
+ self.cube["U"][2] = l_right[::-1]
72
+ for index in range(3):
73
+ self.cube["R"][index][0] = u_bottom[index]
74
+ self.cube["D"][0] = r_left[::-1]
75
+ for index in range(3):
76
+ self.cube["L"][index][2] = d_top[index]
77
+ self.cube["F"] = self._rotate_face(self.cube["F"])
78
+
79
+ def B(self):
80
+ u_top = self.cube["U"][0][::-1]
81
+ r_right = [row[2] for row in self.cube["R"]]
82
+ d_bottom = self.cube["D"][2][::-1]
83
+ l_left = [row[0] for row in self.cube["L"]]
84
+
85
+ self.cube["U"][0] = r_right
86
+ for index in range(3):
87
+ self.cube["R"][index][2] = d_bottom[index]
88
+ self.cube["D"][2] = l_left
89
+ for index in range(3):
90
+ self.cube["L"][index][0] = u_top[index]
91
+ self.cube["B"] = self._rotate_face(self.cube["B"])
92
+
93
+ def R(self):
94
+ u_right = [row[2] for row in self.cube["U"]][::-1]
95
+ b_right = [row[0] for row in self.cube["B"]][::-1]
96
+ d_right = [row[2] for row in self.cube["D"]]
97
+ f_right = [row[2] for row in self.cube["F"]]
98
+
99
+ for index in range(3):
100
+ self.cube["U"][index][2] = f_right[index]
101
+ self.cube["B"][index][0] = u_right[index]
102
+ self.cube["D"][index][2] = b_right[index]
103
+ self.cube["F"][index][2] = d_right[index]
104
+ self.cube["R"] = self._rotate_face(self.cube["R"])
105
+
106
+ def L(self):
107
+ u_left = [row[0] for row in self.cube["U"]]
108
+ f_left = [row[0] for row in self.cube["F"]]
109
+ d_left = [row[0] for row in self.cube["D"]][::-1]
110
+ b_left = [row[2] for row in self.cube["B"]][::-1]
111
+
112
+ for index in range(3):
113
+ self.cube["U"][index][0] = b_left[index]
114
+ self.cube["F"][index][0] = u_left[index]
115
+ self.cube["D"][index][0] = f_left[index]
116
+ self.cube["B"][index][2] = d_left[index]
117
+ self.cube["L"] = self._rotate_face(self.cube["L"])
118
+
119
+ def U(self):
120
+ f_top = self.cube["F"][0].copy()
121
+ l_top = self.cube["L"][0].copy()
122
+ b_top = self.cube["B"][0].copy()
123
+ r_top = self.cube["R"][0].copy()
124
+
125
+ self.cube["F"][0] = r_top
126
+ self.cube["L"][0] = f_top
127
+ self.cube["B"][0] = l_top
128
+ self.cube["R"][0] = b_top
129
+ self.cube["U"] = self._rotate_face(self.cube["U"])
130
+
131
+ def D(self):
132
+ f_bottom = self.cube["F"][2].copy()
133
+ l_bottom = self.cube["L"][2].copy()
134
+ b_bottom = self.cube["B"][2].copy()
135
+ r_bottom = self.cube["R"][2].copy()
136
+
137
+ self.cube["F"][2] = l_bottom
138
+ self.cube["R"][2] = f_bottom
139
+ self.cube["B"][2] = r_bottom
140
+ self.cube["L"][2] = b_bottom
141
+ self.cube["D"] = self._rotate_face(self.cube["D"])
142
+
143
+ def x(self):
144
+ old = {face: [row.copy() for row in self.cube[face]] for face in "ULFRBD"}
145
+
146
+ self.cube["U"] = old["F"]
147
+ self.cube["F"] = old["D"]
148
+ self.cube["D"] = self._rotate_face_twice(old["B"])
149
+ self.cube["B"] = self._rotate_face_twice(old["U"])
150
+ self.cube["R"] = self._rotate_face(old["R"])
151
+ self.cube["L"] = self._rotate_face_counterclockwise(old["L"])
152
+
153
+ def y(self):
154
+ old = {face: [row.copy() for row in self.cube[face]] for face in "ULFRBD"}
155
+
156
+ self.cube["U"] = self._rotate_face(old["U"])
157
+ self.cube["D"] = self._rotate_face_counterclockwise(old["D"])
158
+ self.cube["F"] = old["R"]
159
+ self.cube["R"] = old["B"]
160
+ self.cube["B"] = old["L"]
161
+ self.cube["L"] = old["F"]
162
+
163
+ def z(self):
164
+ old = {face: [row.copy() for row in self.cube[face]] for face in "ULFRBD"}
165
+
166
+ self.cube["U"] = self._rotate_face(old["L"])
167
+ self.cube["R"] = self._rotate_face(old["U"])
168
+ self.cube["D"] = self._rotate_face(old["R"])
169
+ self.cube["L"] = self._rotate_face(old["D"])
170
+ self.cube["F"] = self._rotate_face(old["F"])
171
+ self.cube["B"] = self._rotate_face_counterclockwise(old["B"])
172
+
173
+ def f(self):
174
+ self.B()
175
+ self.z()
176
+
177
+ def b(self):
178
+ self.F()
179
+ self.z()
180
+ self.z()
181
+ self.z()
182
+
183
+ def r(self):
184
+ self.L()
185
+ self.x()
186
+
187
+ def l(self):
188
+ self.R()
189
+ self.x()
190
+ self.x()
191
+ self.x()
192
+
193
+ def u(self):
194
+ self.D()
195
+ self.y()
196
+
197
+ def d(self):
198
+ self.U()
199
+ self.y()
200
+ self.y()
201
+ self.y()
202
+ #endregion
203
+
204
+ def show_cube(self):
205
+ colors = {
206
+ "W": "\033[47m",
207
+ "R": "\033[41m",
208
+ "B": "\033[44m",
209
+ "O": "\033[48;2;255;165;0m",
210
+ "G": "\033[42m",
211
+ "Y": "\033[43m",
212
+ }
213
+ reset = "\033[0m"
214
+
215
+ def face_lines(face: list[list[str]]) -> list[str]:
216
+ return [
217
+ "".join(
218
+ f"{colors[cell]}\033[30m {cell} {reset}"
219
+ for cell in row
220
+ )
221
+ for row in face
222
+ ]
223
+
224
+ top, left, front, right, back, bottom = [
225
+ face_lines(self.cube[face])
226
+ for face in "ULFRBD"
227
+ ]
228
+ blank = " " * 11
229
+
230
+ print("\n".join([
231
+ *[f"{blank}{line}" for line in top],
232
+ " ".join([left[0], front[0], right[0], back[0]]),
233
+ " ".join([left[1], front[1], right[1], back[1]]),
234
+ " ".join([left[2], front[2], right[2], back[2]]),
235
+ *[f"{blank}{line}" for line in bottom],
236
+ ]))
237
+
238
+ def old_pochmann(self):
239
+ #region old pochmann edges
240
+ edgeLetters = ("B","M")
241
+ listEdgeLetters = []
242
+ seenEdges = [("B","M"), ("M","B")]
243
+
244
+ while True:
245
+ while True:
246
+ edgeIndex = EDGE_LETTERS[edgeLetters[0]], EDGE_LETTERS[edgeLetters[1]]
247
+ edgeColours = (self.cube[edgeIndex[0][0]][edgeIndex[0][1]][edgeIndex[0][2]], self.cube[edgeIndex[1][0]][edgeIndex[1][1]][edgeIndex[1][2]])
248
+ edgeLetters = EDGE_COLOURS_TO_LETTERS[edgeColours]
249
+
250
+ if edgeLetters in seenEdges:
251
+ break
252
+
253
+ listEdgeLetters.append(edgeLetters[0])
254
+ seenEdges.append(edgeLetters)
255
+ seenEdges.append((edgeLetters[1], edgeLetters[0]))
256
+
257
+ seenAll = True
258
+ for edgeLetters in EDGE_COLOURS_TO_LETTERS.values():
259
+ if edgeLetters not in seenEdges:
260
+ seenAll = False
261
+ listEdgeLetters.append(edgeLetters[0])
262
+ break
263
+
264
+ if seenAll:
265
+ break
266
+ #endregion
267
+
268
+ #region old pochmann corners
269
+ cornerLetters = ("E","R","A")
270
+ listCornerLetters = []
271
+ seenCorners = [("E","R","A"), ("A","E","R"), ("R","A","E")]
272
+
273
+ while True:
274
+ while True:
275
+ cornerIndex = CORNER_LETTERS[cornerLetters[0]], CORNER_LETTERS[cornerLetters[1]], CORNER_LETTERS[cornerLetters[2]]
276
+ cornerColours = (self.cube[cornerIndex[0][0]][cornerIndex[0][1]][cornerIndex[0][2]], self.cube[cornerIndex[1][0]][cornerIndex[1][1]][cornerIndex[1][2]], self.cube[cornerIndex[2][0]][cornerIndex[2][1]][cornerIndex[2][2]])
277
+ cornerLetters = CORNER_COLOURS_TO_LETTERS[cornerColours]
278
+
279
+ if cornerLetters in seenCorners:
280
+ break
281
+
282
+ listCornerLetters.append(cornerLetters[0])
283
+ seenCorners.append(cornerLetters)
284
+ seenCorners.append((cornerLetters[2], cornerLetters[0], cornerLetters[1]))
285
+ seenCorners.append((cornerLetters[1], cornerLetters[2], cornerLetters[0]))
286
+
287
+ seenAll = True
288
+ for cornerLetters in CORNER_COLOURS_TO_LETTERS.values():
289
+ if cornerLetters not in seenCorners:
290
+ seenAll = False
291
+ listCornerLetters.append(cornerLetters[0])
292
+ break
293
+
294
+ if seenAll:
295
+ break
296
+ #endregion
297
+
298
+ #region remove duplicates
299
+ edgeList = []
300
+ for letter in listEdgeLetters:
301
+ if edgeList and edgeList[-1] == letter:
302
+ edgeList.pop()
303
+ else:
304
+ edgeList.append(letter)
305
+ edgeList = " ".join(edgeList)
306
+
307
+ cornerList = []
308
+ for letter in listCornerLetters:
309
+ if cornerList and cornerList[-1] == letter:
310
+ cornerList.pop()
311
+ else:
312
+ cornerList.append(letter)
313
+ cornerList = " ".join(cornerList)
314
+ #endregion
315
+
316
+ return (edgeList, cornerList)
lercube-0.1.0/dict.py ADDED
@@ -0,0 +1,26 @@
1
+ import json
2
+ from pathlib import Path
3
+
4
+ DATA_DIR = Path(__file__).parent / "json"
5
+
6
+ def load_json_data(filename):
7
+ file_path = DATA_DIR / filename
8
+ with open(file_path, 'r') as f:
9
+ return json.load(f)
10
+
11
+ def load_tuple_key_json_data(filename):
12
+ records = load_json_data(filename)
13
+ return {
14
+ tuple(record["key"]): tuple(record["value"])
15
+ for record in records
16
+ }
17
+
18
+ EDGE_LETTERS = load_json_data("EDGE_LETTERS.json")
19
+ EDGE_COLOURS_TO_LETTERS = load_tuple_key_json_data("EDGE_COLOURS_TO_LETTERS.json")
20
+ EDGE_LETTER_PLACEMENTS = load_json_data("EDGE_LETTER_PLACEMENTS.json")
21
+ EDGE_LETTER_PLACEMENTS_FLIPPED = load_json_data("EDGE_LETTER_PLACEMENTS_FLIPPED.json")
22
+
23
+ CORNER_LETTERS = load_json_data("CORNER_LETTERS.json")
24
+ CORNER_COLOURS_TO_LETTERS = load_tuple_key_json_data("CORNER_COLOURS_TO_LETTERS.json")
25
+ CORNER_LETTER_PLACEMENTS = load_json_data("CORNER_LETTER_PLACEMENTS.json")
26
+ CORNER_LETTER_PLACEMENTS_FLIPPED = load_json_data("CORNER_LETTER_PLACEMENTS_FLIPPED.json")
@@ -0,0 +1,290 @@
1
+ [
2
+ {
3
+ "key": [
4
+ "W",
5
+ "O",
6
+ "B"
7
+ ],
8
+ "value": [
9
+ "A",
10
+ "E",
11
+ "R"
12
+ ]
13
+ },
14
+ {
15
+ "key": [
16
+ "W",
17
+ "B",
18
+ "R"
19
+ ],
20
+ "value": [
21
+ "B",
22
+ "Q",
23
+ "N"
24
+ ]
25
+ },
26
+ {
27
+ "key": [
28
+ "W",
29
+ "R",
30
+ "G"
31
+ ],
32
+ "value": [
33
+ "C",
34
+ "M",
35
+ "J"
36
+ ]
37
+ },
38
+ {
39
+ "key": [
40
+ "W",
41
+ "G",
42
+ "O"
43
+ ],
44
+ "value": [
45
+ "D",
46
+ "I",
47
+ "F"
48
+ ]
49
+ },
50
+ {
51
+ "key": [
52
+ "O",
53
+ "B",
54
+ "W"
55
+ ],
56
+ "value": [
57
+ "E",
58
+ "R",
59
+ "A"
60
+ ]
61
+ },
62
+ {
63
+ "key": [
64
+ "O",
65
+ "W",
66
+ "G"
67
+ ],
68
+ "value": [
69
+ "F",
70
+ "D",
71
+ "I"
72
+ ]
73
+ },
74
+ {
75
+ "key": [
76
+ "O",
77
+ "G",
78
+ "Y"
79
+ ],
80
+ "value": [
81
+ "G",
82
+ "L",
83
+ "U"
84
+ ]
85
+ },
86
+ {
87
+ "key": [
88
+ "O",
89
+ "Y",
90
+ "B"
91
+ ],
92
+ "value": [
93
+ "H",
94
+ "X",
95
+ "S"
96
+ ]
97
+ },
98
+ {
99
+ "key": [
100
+ "G",
101
+ "O",
102
+ "W"
103
+ ],
104
+ "value": [
105
+ "I",
106
+ "F",
107
+ "D"
108
+ ]
109
+ },
110
+ {
111
+ "key": [
112
+ "G",
113
+ "W",
114
+ "R"
115
+ ],
116
+ "value": [
117
+ "J",
118
+ "C",
119
+ "M"
120
+ ]
121
+ },
122
+ {
123
+ "key": [
124
+ "G",
125
+ "R",
126
+ "Y"
127
+ ],
128
+ "value": [
129
+ "K",
130
+ "P",
131
+ "V"
132
+ ]
133
+ },
134
+ {
135
+ "key": [
136
+ "G",
137
+ "Y",
138
+ "O"
139
+ ],
140
+ "value": [
141
+ "L",
142
+ "U",
143
+ "G"
144
+ ]
145
+ },
146
+ {
147
+ "key": [
148
+ "R",
149
+ "G",
150
+ "W"
151
+ ],
152
+ "value": [
153
+ "M",
154
+ "J",
155
+ "C"
156
+ ]
157
+ },
158
+ {
159
+ "key": [
160
+ "R",
161
+ "W",
162
+ "B"
163
+ ],
164
+ "value": [
165
+ "N",
166
+ "B",
167
+ "Q"
168
+ ]
169
+ },
170
+ {
171
+ "key": [
172
+ "R",
173
+ "B",
174
+ "Y"
175
+ ],
176
+ "value": [
177
+ "O",
178
+ "T",
179
+ "W"
180
+ ]
181
+ },
182
+ {
183
+ "key": [
184
+ "R",
185
+ "Y",
186
+ "G"
187
+ ],
188
+ "value": [
189
+ "P",
190
+ "V",
191
+ "K"
192
+ ]
193
+ },
194
+ {
195
+ "key": [
196
+ "B",
197
+ "R",
198
+ "W"
199
+ ],
200
+ "value": [
201
+ "Q",
202
+ "N",
203
+ "B"
204
+ ]
205
+ },
206
+ {
207
+ "key": [
208
+ "B",
209
+ "W",
210
+ "O"
211
+ ],
212
+ "value": [
213
+ "R",
214
+ "A",
215
+ "E"
216
+ ]
217
+ },
218
+ {
219
+ "key": [
220
+ "B",
221
+ "O",
222
+ "Y"
223
+ ],
224
+ "value": [
225
+ "S",
226
+ "H",
227
+ "X"
228
+ ]
229
+ },
230
+ {
231
+ "key": [
232
+ "B",
233
+ "Y",
234
+ "R"
235
+ ],
236
+ "value": [
237
+ "T",
238
+ "W",
239
+ "O"
240
+ ]
241
+ },
242
+ {
243
+ "key": [
244
+ "Y",
245
+ "O",
246
+ "G"
247
+ ],
248
+ "value": [
249
+ "U",
250
+ "G",
251
+ "L"
252
+ ]
253
+ },
254
+ {
255
+ "key": [
256
+ "Y",
257
+ "G",
258
+ "R"
259
+ ],
260
+ "value": [
261
+ "V",
262
+ "K",
263
+ "P"
264
+ ]
265
+ },
266
+ {
267
+ "key": [
268
+ "Y",
269
+ "R",
270
+ "B"
271
+ ],
272
+ "value": [
273
+ "W",
274
+ "O",
275
+ "T"
276
+ ]
277
+ },
278
+ {
279
+ "key": [
280
+ "Y",
281
+ "B",
282
+ "O"
283
+ ],
284
+ "value": [
285
+ "X",
286
+ "S",
287
+ "H"
288
+ ]
289
+ }
290
+ ]