XMWAI 0.4.2__py3-none-any.whl → 0.4.5__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 XMWAI might be problematic. Click here for more details.

XMWAI/__init__.py CHANGED
@@ -2,10 +2,12 @@ from .core import story, photo, reply, poem, crack, show # 从子模块导入
2
2
  from .magic_core import birthday # 从子模块导入函数到顶层
3
3
  from .bomb_core import bomb # 从子模块导入函数到顶层
4
4
  from .idiom_core import idiom, searchIdiom, get_json_path # 从子模块导入函数到顶层
5
- from .trial_class import make, get_file_content_as_base64, save_pic, detect_windows_scale, get_file_content_as_base64_2, cartoon # 从子模块导入函数到顶层
5
+ from .trial_class import make, get_file_content_as_base64, save_pic, detect_scale, open_image, get_file_content_as_base64_2, cartoon # 从子模块导入函数到顶层
6
6
  from .web_core import burger, cookbook # 从子模块导入函数到顶层
7
+ from .snake_core import SnakeGame
8
+
7
9
 
8
10
  __all__ = ["story", "photo", "reply", "poem", 'birthday', 'bomb',
9
11
  "idiom", "searchIdiom", "get_json_path", "crack", "show",
10
- "make", "get_file_content_as_base64", "save_pic", "detect_windows_scale",
11
- "get_file_content_as_base64_2", "cartoon", "burger", "cookbook"] # 可选:明确导出的内容
12
+ "make", "get_file_content_as_base64", "save_pic", "detect_scale", "open_image",
13
+ "get_file_content_as_base64_2", "cartoon", "burger", "cookbook", "SnakeGame"] # 可选:明确导出的内容
XMWAI/assets/1.png ADDED
Binary file
File without changes
XMWAI/assets/g.png ADDED
Binary file
XMWAI/assets/h.png ADDED
Binary file
XMWAI/assets/l.png ADDED
Binary file
XMWAI/assets/m.png ADDED
Binary file
XMWAI/assets/s.png ADDED
Binary file
XMWAI/assets/t.png ADDED
Binary file
XMWAI/snake_core.py ADDED
@@ -0,0 +1,389 @@
1
+ import cv2
2
+ import math
3
+ import random
4
+ import cvzone
5
+ import numpy as np
6
+ from cvzone.HandTrackingModule import HandDetector
7
+ from PIL import ImageFont, ImageDraw, Image
8
+ import os
9
+ import time
10
+ import importlib.resources as pkg_resources
11
+
12
+ # 引入包内资源
13
+ import XMWAI.assets as assets
14
+
15
+
16
+ def get_asset_path(filename: str) -> str:
17
+ """获取包内资源的路径"""
18
+ with pkg_resources.path(assets, filename) as p:
19
+ return str(p)
20
+
21
+
22
+ class SnakeGame:
23
+ def __init__(self, width=720, height=720, snakeInitLength=150, snakeGrowth=50,
24
+ snakeLineWidth=10, snakeHeadSize=15, foodPaths=None, foodNames=None, foodScores=None,
25
+ obstaclePaths=None, fontPath=None):
26
+
27
+ self.resolution = (width, height)
28
+ self.snakeInitLength = snakeInitLength
29
+ self._snakeGrowth = snakeGrowth
30
+ self._snakeHeadSize = snakeHeadSize
31
+ self._foodScores = foodScores if foodScores is not None else [3, 2, 1]
32
+ self.snakeLineWidth = snakeLineWidth
33
+
34
+ # 字体路径(默认取包内字体)
35
+ self.fontPath = fontPath or get_asset_path("微软雅黑.ttf")
36
+
37
+ # 默认资源路径
38
+ if foodPaths is None:
39
+ foodPaths = [get_asset_path("h.png"),
40
+ get_asset_path("s.png"),
41
+ get_asset_path("t.png")]
42
+ if foodNames is None:
43
+ foodNames = ["汉堡", "薯条", "甜甜圈"]
44
+ if obstaclePaths is None:
45
+ obstaclePaths = [get_asset_path("g.png"),
46
+ get_asset_path("l.png"),
47
+ get_asset_path("m.png")]
48
+
49
+ self.foodPaths = foodPaths
50
+ self.foodNames = foodNames
51
+ self.obstaclePaths = obstaclePaths
52
+
53
+ self.cap = None
54
+ self.detector = None
55
+ self.snake = None
56
+ self.foodManager = None
57
+ self.obstacleManager = None
58
+ self.img = None
59
+ self.overlayTexts = []
60
+
61
+ self.timer = 30
62
+ self.start_time = None
63
+
64
+ self._init_game_objects()
65
+ self.open_window()
66
+
67
+ # ----------- 属性自动同步 -----------
68
+ @property
69
+ def snakeHeadSize(self):
70
+ return self._snakeHeadSize
71
+
72
+ @snakeHeadSize.setter
73
+ def snakeHeadSize(self, value):
74
+ self._snakeHeadSize = value
75
+ if self.snake:
76
+ self.snake.headSize = value
77
+
78
+ @property
79
+ def foodScores(self):
80
+ return self._foodScores
81
+
82
+ @foodScores.setter
83
+ def foodScores(self, scores):
84
+ self._foodScores = scores
85
+ if self.foodManager:
86
+ self.foodManager.foodScores = scores
87
+
88
+ @property
89
+ def snakeGrowth(self):
90
+ return self._snakeGrowth
91
+
92
+ @snakeGrowth.setter
93
+ def snakeGrowth(self, value):
94
+ self._snakeGrowth = value
95
+
96
+ # ---------------- 工具函数 ----------------
97
+ def _putChineseText(self, img, text, pos, fontSize=40, color=(0, 0, 255)):
98
+ img_pil = Image.fromarray(img)
99
+ draw = ImageDraw.Draw(img_pil)
100
+ try:
101
+ font = ImageFont.truetype(self.fontPath, fontSize)
102
+ except OSError:
103
+ font = ImageFont.load_default()
104
+ draw.text(pos, text, font=font, fill=color)
105
+ return np.array(img_pil)
106
+
107
+ def _init_game_objects(self):
108
+ self.snake = self.Snake(color=(0, 0, 255), initLength=self.snakeInitLength,
109
+ lineWidth=self.snakeLineWidth, headSize=self._snakeHeadSize)
110
+ self.foodManager = self.FoodManager(
111
+ self.foodPaths, self.foodNames, self._foodScores)
112
+ self.obstacleManager = self.ObstacleManager(self.obstaclePaths)
113
+ self.obstacleManager.randomObstacles()
114
+
115
+ def _render_frame(self, show_food=True, show_obstacle=True):
116
+ success, self.img = self.cap.read()
117
+ if not success:
118
+ return
119
+ self.img = cv2.flip(self.img, 1)
120
+ hands, self.img = self.detector.findHands(self.img, flipType=False)
121
+ player_head = tuple(hands[0]['lmList'][8][0:2]) if hands else None
122
+
123
+ if not self.snake.gameOver and player_head:
124
+ self.snake.update(self.img, player_head, self.obstacleManager)
125
+
126
+ if not self.snake.gameOver and player_head and show_food:
127
+ cx, cy = player_head
128
+ rx, ry = self.foodManager.foodPoint
129
+ w, h = self.foodManager.wFood, self.foodManager.hFood
130
+ if rx - w//2 <= cx <= rx + w//2 and ry - h//2 <= cy <= ry + h//2:
131
+ self.snake.score += self.foodManager.foodScores[self.foodManager.foodIndex]
132
+ self.snake.allowedLength += self._snakeGrowth
133
+ self.foodManager.randomFoodLocation(self.obstacleManager)
134
+
135
+ if show_obstacle:
136
+ self.img = self.obstacleManager.draw(self.img)
137
+ self.obstacleManager.moveObstacles(
138
+ self.resolution[0], self.resolution[1])
139
+
140
+ if show_food:
141
+ self.img = self.foodManager.draw(self.img)
142
+
143
+ # ---------------- 对外接口 ----------------
144
+ def open_window(self):
145
+ self.cap = cv2.VideoCapture(0)
146
+ self.cap.set(3, self.resolution[0])
147
+ self.cap.set(4, self.resolution[1])
148
+ self.detector = HandDetector(detectionCon=0.7, maxHands=1)
149
+ success, self.img = self.cap.read()
150
+ if not success:
151
+ print("摄像头打开失败")
152
+ return
153
+ self.img = cv2.flip(self.img, 1)
154
+ cv2.imshow("AI Snake", self.img)
155
+ cv2.waitKey(1)
156
+
157
+ def reset_game(self):
158
+ self.snake.reset()
159
+ self.snake.headSize = self._snakeHeadSize
160
+ self.obstacleManager.randomObstacles()
161
+ self.foodManager.foodScores = self._foodScores
162
+ self.foodManager.randomFoodLocation(self.obstacleManager)
163
+ self.start_time = time.time()
164
+ self.timer = 30
165
+ if self.cap is None or not self.cap.isOpened():
166
+ self.open_window()
167
+
168
+ def gameover(self, path=None, size=(100, 100)):
169
+ if path is None:
170
+ path = get_asset_path("1.png")
171
+
172
+ if os.path.exists(path):
173
+ gameover_img = cv2.imread(path)
174
+ gameover_img = cv2.resize(gameover_img, self.resolution)
175
+ else:
176
+ gameover_img = np.zeros(
177
+ (self.resolution[1], self.resolution[0], 3), np.uint8)
178
+ cv2.putText(gameover_img, "Game Over Image Missing!", (50, 100),
179
+ cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 3)
180
+
181
+ gameover_img = self._putChineseText(
182
+ gameover_img, f"最终分数:{self.snake.score}", size, 40, (68, 84, 106))
183
+
184
+ while True:
185
+ cv2.imshow("AI Snake", gameover_img)
186
+ key = cv2.waitKey(0) & 0xFF
187
+ if key == ord('r'): # 重启游戏
188
+ self.reset_game()
189
+ self.start()
190
+ break
191
+ elif key == ord('q'): # 退出游戏
192
+ if self.cap is not None:
193
+ self.cap.release()
194
+ cv2.destroyAllWindows()
195
+ break
196
+
197
+ def start(self):
198
+ if self.cap is None or not self.cap.isOpened():
199
+ self.open_window()
200
+ self.start_time = time.time()
201
+ self.timer = 30
202
+
203
+ while True:
204
+ if self.snake.gameOver or self.timer == 0:
205
+ self.gameover()
206
+ break
207
+
208
+ self._render_frame(show_food=True, show_obstacle=True)
209
+ elapsed = int(time.time() - self.start_time)
210
+ self.timer = max(0, 30 - elapsed)
211
+
212
+ self.overlayTexts = [
213
+ (f'玩家分数:{self.snake.score}', (50, 50), 30, (255, 0, 255)),
214
+ (f'倒计时:{self.timer} 秒', (50, 120), 30, (255, 0, 255))
215
+ ]
216
+
217
+ if self.img is not None:
218
+ img_copy = self.img.copy()
219
+ for txt, pos, size, color in self.overlayTexts:
220
+ img_copy = self._putChineseText(
221
+ img_copy, txt, pos, size, color)
222
+ cv2.imshow("AI Snake", img_copy)
223
+
224
+ key = cv2.waitKey(1)
225
+ if key == ord('r'):
226
+ self.reset_game()
227
+ elif key == ord('q'):
228
+ if self.cap is not None:
229
+ self.cap.release()
230
+ cv2.destroyAllWindows()
231
+ break
232
+
233
+ # ---------------- 内部类:蛇 ----------------
234
+ class Snake:
235
+ def __init__(self, color, initLength=150, lineWidth=10, headSize=15):
236
+ self.points = []
237
+ self.currentLength = 0
238
+ self.allowedLength = initLength
239
+ self.previousHead = None
240
+ self.score = 0
241
+ self.color = color
242
+ self.gameOver = False
243
+ self.lineWidth = lineWidth
244
+ self.headSize = headSize
245
+
246
+ def reset(self):
247
+ self.points = []
248
+ self.currentLength = 0
249
+ self.allowedLength = 150
250
+ self.previousHead = None
251
+ self.score = 0
252
+ self.gameOver = False
253
+
254
+ def update(self, imgMain, currentHead, obstacleManager=None):
255
+ if self.gameOver:
256
+ return
257
+ cx, cy = currentHead
258
+ if cx is None or cy is None:
259
+ return
260
+ if self.previousHead is None:
261
+ self.previousHead = (cx, cy)
262
+ px, py = self.previousHead
263
+
264
+ alpha = 0.7
265
+ cx = int(px * (1 - alpha) + cx * alpha)
266
+ cy = int(py * (1 - alpha) + cy * alpha)
267
+
268
+ maxStep = 50
269
+ dx = cx - px
270
+ dy = cy - py
271
+ distance = math.hypot(dx, dy)
272
+ if distance > maxStep:
273
+ steps = int(distance / maxStep)
274
+ for i in range(1, steps + 1):
275
+ ix = int(px + dx * i / steps)
276
+ iy = int(py + dy * i / steps)
277
+ self.points.append((ix, iy))
278
+ self.currentLength += maxStep
279
+ else:
280
+ self.points.append((cx, cy))
281
+ self.currentLength += distance
282
+
283
+ self.previousHead = (cx, cy)
284
+
285
+ while self.currentLength > self.allowedLength:
286
+ if len(self.points) > 1:
287
+ removed_dx = self.points[1][0] - self.points[0][0]
288
+ removed_dy = self.points[1][1] - self.points[0][1]
289
+ removed_dist = math.hypot(removed_dx, removed_dy)
290
+ self.currentLength -= removed_dist
291
+ self.points.pop(0)
292
+
293
+ for i in range(1, len(self.points)):
294
+ cv2.line(
295
+ imgMain, self.points[i-1], self.points[i], self.color, self.lineWidth)
296
+
297
+ snakeHeadColor = (random.randint(0, 255), random.randint(
298
+ 0, 255), random.randint(0, 255))
299
+ cv2.circle(imgMain, (cx, cy), self.headSize,
300
+ snakeHeadColor, cv2.FILLED)
301
+
302
+ h, w, _ = imgMain.shape
303
+ margin = 5
304
+ if cx <= margin or cx >= w - margin or cy <= margin or cy >= h - margin:
305
+ self.gameOver = True
306
+
307
+ if obstacleManager:
308
+ for ox, oy, ow, oh, *_ in obstacleManager.obstacles:
309
+ if ox <= cx <= ox + ow and oy <= cy <= oy + oh:
310
+ self.gameOver = True
311
+
312
+ # ---------------- 内部类:食物 ----------------
313
+ class FoodManager:
314
+ def __init__(self, foodPaths, foodNames, foodScores):
315
+ self.foodImages = []
316
+ for path in foodPaths:
317
+ if os.path.exists(path):
318
+ self.foodImages.append(
319
+ cv2.imread(path, cv2.IMREAD_UNCHANGED))
320
+ else:
321
+ self.foodImages.append(np.zeros((50, 50, 4), np.uint8))
322
+ self.foodNames = foodNames
323
+ self.foodScores = foodScores
324
+ self.foodIndex = 0
325
+ self.hFood, self.wFood = 0, 0
326
+ self.foodPoint = 0, 0
327
+ self.randomFoodLocation()
328
+
329
+ def randomFoodLocation(self, obstacleManager=None):
330
+ max_attempts = 100
331
+ for _ in range(max_attempts):
332
+ self.foodPoint = random.randint(
333
+ 50, 670), random.randint(50, 670)
334
+ self.foodIndex = random.randint(0, len(self.foodImages)-1)
335
+ self.hFood, self.wFood, _ = self.foodImages[self.foodIndex].shape
336
+ if obstacleManager:
337
+ overlap = False
338
+ for ox, oy, ow, oh, *_ in obstacleManager.obstacles:
339
+ if ox < self.foodPoint[0] < ox + ow and oy < self.foodPoint[1] < oy + oh:
340
+ overlap = True
341
+ break
342
+ if not overlap:
343
+ return
344
+ return
345
+
346
+ def draw(self, imgMain):
347
+ rx, ry = self.foodPoint
348
+ imgMain = cvzone.overlayPNG(imgMain, self.foodImages[self.foodIndex],
349
+ (rx - self.wFood//2, ry - self.hFood//2))
350
+ return imgMain
351
+
352
+ # ---------------- 内部类:障碍 ----------------
353
+ class ObstacleManager:
354
+ def __init__(self, obstaclePaths):
355
+ self.obstacleImages = []
356
+ for path in obstaclePaths:
357
+ if os.path.exists(path):
358
+ self.obstacleImages.append(
359
+ cv2.imread(path, cv2.IMREAD_UNCHANGED))
360
+ else:
361
+ self.obstacleImages.append(np.zeros((50, 50, 4), np.uint8))
362
+ self.obstacles = []
363
+
364
+ def randomObstacles(self):
365
+ self.obstacles.clear()
366
+ for img in self.obstacleImages:
367
+ h, w, _ = img.shape
368
+ x = random.randint(150, 600)
369
+ y = random.randint(50, 600)
370
+ dx = random.choice([-5, 5])
371
+ dy = random.choice([-5, 5])
372
+ self.obstacles.append([x, y, w, h, dx, dy, img])
373
+
374
+ def moveObstacles(self, wMax, hMax):
375
+ for obs in self.obstacles:
376
+ x, y, ow, oh, dx, dy, img = obs
377
+ x += dx
378
+ y += dy
379
+ if x <= 0 or x + ow >= wMax:
380
+ dx *= -1
381
+ if y <= 0 or y + oh >= hMax:
382
+ dy *= -1
383
+ obs[0], obs[1], obs[4], obs[5] = x, y, dx, dy
384
+
385
+ def draw(self, imgMain):
386
+ for obs in self.obstacles:
387
+ x, y, w, h, dx, dy, img = obs
388
+ imgMain = cvzone.overlayPNG(imgMain, img, (int(x), int(y)))
389
+ return imgMain
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: XMWAI
3
- Version: 0.4.2
3
+ Version: 0.4.5
4
4
  Summary: Small code King AI related library
5
5
  Home-page: https://github.com/Tonykai88/XMWAI.git
6
6
  Author: pydevelopment
@@ -17,6 +17,7 @@ Requires-Dist: opencv-python>=3.4.18.65
17
17
  Requires-Dist: numpy>=1.26.0
18
18
  Requires-Dist: flask>=3.1.0
19
19
  Requires-Dist: pyecharts>=2.0.8
20
+ Requires-Dist: cvzone>=1.6.1
20
21
  Dynamic: author
21
22
  Dynamic: author-email
22
23
  Dynamic: classifier
@@ -1,10 +1,20 @@
1
- XMWAI/__init__.py,sha256=-0GLZK0X3vEuTbu5n2AX3cWw6SXtJTERtBIAssZq41o,908
1
+ XMWAI/__init__.py,sha256=RFHMM1PF3ELvHSMeKe2G2E0astGPum2ezuf8Hjw4D6g,968
2
2
  XMWAI/bomb_core.py,sha256=h2ZPH3SuoG2L_XOf1dcK8p3lhw5QzhneWl2yMLj1RiU,1819
3
3
  XMWAI/core.py,sha256=rOXj7FnewSdnzBcFLjpnBtrOTCsvMfiycIcdPDagxho,10012
4
4
  XMWAI/idiom_core.py,sha256=yU-VHhqqoutVm6GVikcjL3m9yuB1hUsFBpPYvwY4n5g,1689
5
5
  XMWAI/magic_core.py,sha256=Ms4b12PJ8rjsmceg1VUqWCWx2ebvdhLp4sIF6K_Vaok,3491
6
+ XMWAI/snake_core.py,sha256=oKcjGwEm2MB7Q8gfRo80NRqul20LGiEIe3--4k1-E74,14782
6
7
  XMWAI/trial_class.py,sha256=fPsl7BZvhzch2FOIG4azr999kjtoly53Acm3LqL8f98,9724
7
8
  XMWAI/web_core.py,sha256=7awPg1kYW3lYrbgylqJvUF3g050bn6H21PgmQ7Kv1wA,10927
9
+ XMWAI/assets/1.png,sha256=eEuKH_M_q3tc_O2bYnuLOsRP-NlJHIbNg0pgrKXEEjw,139720
10
+ XMWAI/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ XMWAI/assets/g.png,sha256=hr9hlKJ7y95X-g6-tllrzDNgL1WQkbq5cA5L4jASEAM,11686
12
+ XMWAI/assets/h.png,sha256=qO-kJJOPA8qUth5rqLeOVa_6_n7tU-ABQ14O0EW_YCE,14929
13
+ XMWAI/assets/l.png,sha256=Urm6LxH33HID6ZZbs2oMViUk4GiZ3upLPdsrNU8FlP0,9921
14
+ XMWAI/assets/m.png,sha256=4tl9Rb2JoMD7XLMj3w8jg-92y6D7O-1u0sZCYEoeUtk,10303
15
+ XMWAI/assets/s.png,sha256=v_qmHGmSPhG838vXQdUR2ZX_Z5KLI8T46kaR4P_ktUg,15120
16
+ XMWAI/assets/t.png,sha256=GebzA2UWhXn4u62UeuUjitdpwnJvnxfZ2z_4MFlxvm8,12838
17
+ XMWAI/assets/微软雅黑.ttf,sha256=dpc4EmGE1ojdwHjfIwTdr3olyEDAk3FwyreQSC9AdQ8,15043584
8
18
  XMWAI/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
19
  XMWAI/file/idiom.json,sha256=HUtPRUzhxBbWoasjadbmbA_5ngQ5AXLu9weQSZ4hzhk,10319857
10
20
  XMWAI/gif/0.gif,sha256=LGpAReVTyZEb1J-bWYTpxxHbIxmLJ-3wA0lw4cBqdsY,303
@@ -107,8 +117,8 @@ XMWAI/static/images/tomato.png,sha256=FEOEAOdUhW_BDFgTpxOkYc0I5Iu29_gtHb3RIPEej0
107
117
  XMWAI/templates/burger.html,sha256=vDnxpSW8phetyScySsalScZnFKl3LNpy5lJjKxGXgbI,3320
108
118
  XMWAI/templates/nutrition_pie.html,sha256=yJVXI28i-UfvF0xOXGSNLMb8oCJNhh2J3zoRDr5_7DM,5567
109
119
  XMWAI/templates/创意菜谱.html,sha256=RcDgH58QLyUJ9A59wobu3wvchGBY1snVsXcZQZam5M0,4805
110
- xmwai-0.4.2.dist-info/licenses/LICENSE.txt,sha256=bcaIQMrIhdQ3O-PoZlexjmW6h-wLGvHxh5Oksl4ohtc,1066
111
- xmwai-0.4.2.dist-info/METADATA,sha256=6GJQaqzQytoahOWk1ssi_sezcfONReHCN5h2rj9rtJQ,1197
112
- xmwai-0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
113
- xmwai-0.4.2.dist-info/top_level.txt,sha256=yvGcDI-sggK5jqd9wz0saipZvk3oIE3hNGHlqUjxf0c,6
114
- xmwai-0.4.2.dist-info/RECORD,,
120
+ xmwai-0.4.5.dist-info/licenses/LICENSE.txt,sha256=bcaIQMrIhdQ3O-PoZlexjmW6h-wLGvHxh5Oksl4ohtc,1066
121
+ xmwai-0.4.5.dist-info/METADATA,sha256=PBWm8ONw169ZRDJr5VSBWpGwb-XGsjGMrjzwaZs_umw,1227
122
+ xmwai-0.4.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
123
+ xmwai-0.4.5.dist-info/top_level.txt,sha256=yvGcDI-sggK5jqd9wz0saipZvk3oIE3hNGHlqUjxf0c,6
124
+ xmwai-0.4.5.dist-info/RECORD,,
File without changes