Skip to content

Commit 58c60dc

Browse files
authored
Merge pull request #92 from ljzloser/master
feat: 添加LLM插件系统和游戏控制命令
2 parents bd6d11e + eff026f commit 58c60dc

File tree

12 files changed

+1614
-17
lines changed

12 files changed

+1614
-17
lines changed

src/main.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
# 插件系统(新)
1919
from plugin_sdk import GameServerBridge
2020
from plugin_manager.app_paths import get_env_for_subprocess
21-
from shared_types.commands import NewGameCommand
21+
from shared_types.commands import NewGameCommand, MouseClickCommand
22+
from shared_types.enums import GameLevel
2223
import subprocess
2324

2425
os.environ["QT_FONT_DPI"] = "96"
@@ -199,15 +200,40 @@ def cli_check_file(file_path: str) -> int:
199200
ui._plugin_process = plugin_process # 保存引用,防止被 GC
200201

201202
GameServerBridge.instance().start()
202-
203+
203204
# 注册控制命令处理器(自动在主线程执行)
204205
def handle_new_game(cmd: NewGameCommand):
205-
print(f"[NewGameCommand] rows={cmd.rows}, cols={cmd.cols}, mines={cmd.mines}")
206-
ui.setBoard_and_start(cmd.rows, cmd.cols, cmd.mines)
206+
"""处理新游戏命令"""
207207
from lib_zmq_plugins.shared.base import CommandResponse
208+
209+
# 根据 level 确定参数
210+
if cmd.level == GameLevel.BEGINNER.value:
211+
rows, cols, mines = 8, 8, 10
212+
elif cmd.level == GameLevel.INTERMEDIATE.value:
213+
rows, cols, mines = 16, 16, 40
214+
elif cmd.level == GameLevel.EXPERT.value:
215+
rows, cols, mines = 16, 30, 99
216+
else:
217+
# 自定义模式,使用传入的参数
218+
rows, cols, mines = cmd.rows, cmd.cols, cmd.mines
219+
220+
print(
221+
f"[NewGameCommand] level={cmd.level}, rows={rows}, cols={cols}, mines={mines}")
222+
ui.setBoard_and_start(rows, cols, mines)
208223
return CommandResponse(request_id=cmd.request_id, success=True)
209-
224+
225+
def handle_mouse_click(cmd: MouseClickCommand):
226+
"""处理鼠标点击命令"""
227+
from lib_zmq_plugins.shared.base import CommandResponse
228+
229+
print(
230+
f"[MouseClickCommand] row={cmd.row}, col={cmd.col}, button={cmd.button}")
231+
success = ui.execute_cell_click(cmd.row, cmd.col, cmd.button)
232+
return CommandResponse(request_id=cmd.request_id, success=success)
233+
210234
GameServerBridge.instance().register_handler(NewGameCommand, handle_new_game)
235+
GameServerBridge.instance().register_handler(
236+
MouseClickCommand, handle_mouse_click)
211237

212238
# _translate = QtCore.QCoreApplication.translate
213239
hwnd = int(ui.mainWindow.winId())

src/mineSweeperGUI.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# from PyQt5.QtWidgets import QApplication, QFileDialog, QWidget
88
import gameDefinedParameter
99
from plugin_sdk.server_bridge import GameServerBridge
10-
from shared_types.events import VideoSaveEvent
10+
from shared_types.events import VideoSaveEvent, BoardUpdateEvent, GameStatusChangeEvent
1111
import superGUI
1212
import gameAbout
1313
import gameSettings
@@ -37,6 +37,7 @@
3737

3838

3939
class MineSweeperGUI(MineSweeperVideoPlayer):
40+
4041
def __init__(self, MainWindow: MainWindow, args):
4142
self.mainWindow = MainWindow
4243
self.checksum_guard = metaminesweeper_checksum.ChecksumGuard()
@@ -219,6 +220,8 @@ def game_state(self):
219220
@game_state.setter
220221
def game_state(self, game_state: str):
221222
# print(self._game_state, " -> " ,game_state)
223+
last_state = self._game_state
224+
222225
match self._game_state:
223226
case "playing":
224227
self.try_append_evfs(game_state)
@@ -252,8 +255,31 @@ def game_state(self, game_state: str):
252255
self.label.paint_cursor = False
253256
self.label.paintProbability = False
254257
self.num_bar_ui.QWidget.close()
258+
255259
self._game_state = game_state
256260

261+
# 发送游戏状态变化事件
262+
state_map = {
263+
"ready": 1,
264+
"playing": 2,
265+
"win": 3,
266+
"fail": 4,
267+
"joking": 2, # joking 也视为游戏中
268+
"jowin": 3,
269+
"jofail": 4,
270+
"show": 5,
271+
"study": 6,
272+
"display": 7,
273+
"showdisplay": 8,
274+
}
275+
if last_state != game_state:
276+
event = GameStatusChangeEvent(
277+
last_status=state_map.get(last_state, 0),
278+
current_status=state_map.get(game_state, 0),
279+
)
280+
GameServerBridge.instance().send_event(event)
281+
self._send_board_update_event()
282+
257283
@property
258284
def row(self):
259285
return self._row
@@ -477,6 +503,54 @@ def mineNumWheel(self, i):
477503
# self.timer_mine_num.setSingleShot(True)
478504
# self.timer_mine_num.start(3000)
479505

506+
def _send_board_update_event(self):
507+
"""发送棋盘更新事件给插件"""
508+
try:
509+
ms_board = self.label.ms_board
510+
# 将 game_board 转换为列表格式
511+
game_board_list = []
512+
for row in ms_board.game_board:
513+
game_board_list.append(list(row))
514+
515+
event = BoardUpdateEvent(
516+
rows=self.row,
517+
cols=self.column,
518+
game_board=game_board_list,
519+
mines_remaining=self.mineUnFlagedNum,
520+
game_time=ms_board.time if hasattr(ms_board, 'time') else 0.0,
521+
)
522+
GameServerBridge.instance().send_event(event)
523+
except Exception:
524+
pass # 忽略发送失败
525+
526+
def execute_cell_click(self, row: int, col: int, button: int):
527+
"""
528+
执行格子点击(供外部命令调用)
529+
530+
Args:
531+
row: 行索引(从 0 开始)
532+
col: 列索引(从 0 开始)
533+
button: 鼠标按钮(0=左键, 1=中键, 2=右键)
534+
"""
535+
if row < 0 or row >= self.row or col < 0 or col >= self.column:
536+
return False
537+
x = row * self.pixSize
538+
y = col * self.pixSize
539+
if button == 0:
540+
self.mineAreaLeftPressed(x, y)
541+
self.mineAreaLeftRelease(x, y)
542+
elif button == 1:
543+
self.mineAreaLeftPressed(x, y)
544+
self.mineAreaLeftAndRightPressed(x, y)
545+
self.mineAreaRightRelease(x, y)
546+
self.mineAreaLeftRelease(x, y)
547+
else:
548+
self.mineAreaRightPressed(x, y)
549+
self.mineAreaRightRelease(x, y)
550+
551+
self._send_board_update_event()
552+
return True
553+
480554
def gameStart(self):
481555
# 画界面,但是不埋雷。等价于点脸、f2、设置确定后的效果
482556
self.mineUnFlagedNum = self.minenum # 没有标出的雷,显示在左上角
@@ -571,6 +645,9 @@ def gameFinished(self):
571645
event = VideoSaveEvent(**data)
572646
GameServerBridge.instance().send_event(event)
573647

648+
# 发送棋盘更新事件,让插件知道最终状态
649+
self._send_board_update_event()
650+
574651
def gameWin(self): # 成功后改脸和状态变量,停时间
575652
self.timer_10ms.stop()
576653
self.score_board_manager.editing_row = -1

src/mineSweeperGUIEvent.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def mineAreaLeftRelease(self, i, j):
6969
return
7070
else:
7171
self.label.update()
72+
self._send_board_update_event()
7273
self.set_face(14)
7374
elif self.game_state == 'playing' or self.game_state == 'joking':
7475
# 如果是游戏中,且是左键抬起(不是双击),且是在10上,且在局面内,则用ai劫持、处理下
@@ -88,6 +89,7 @@ def mineAreaLeftRelease(self, i, j):
8889
self.label.update()
8990
return
9091
self.label.update()
92+
self._send_board_update_event()
9193
self.set_face(14)
9294

9395
elif self.game_state == 'show':
@@ -121,6 +123,7 @@ def mineAreaRightRelease(self, i, j):
121123
self.chording_ai(i // self.pixSize, j // self.pixSize)
122124
self.label.ms_board.step('rr', (i, j))
123125
self.label.update()
126+
self._send_board_update_event()
124127
self.set_face(14)
125128
elif self.game_state == 'show':
126129
# 看概率时,所有操作都移出局面外
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""
2+
llm_minesweeper_controller - 使用requests进行反向控制扫雷并处理func calling的插件
3+
"""
4+
from __future__ import annotations
5+
6+
from .plugin import LlmMinesweeperControllerPlugin
7+
8+
__all__ = ["LlmMinesweeperControllerPlugin"]

0 commit comments

Comments
 (0)