You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

logic.py 17 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. import os
  2. from abc import abstractmethod
  3. from typing import List, Union, Callable
  4. import threading
  5. import logging
  6. import proto.MessageType_pb2 as MessageType
  7. import proto.Message2Server_pb2 as Message2Server
  8. import proto.Message2Clients_pb2 as Message2Clients
  9. import PyAPI.structures as THUAI6
  10. from PyAPI.utils import Proto2THUAI6, AssistFunction
  11. from PyAPI.DebugAPI import StudentDebugAPI, TrickerDebugAPI
  12. from PyAPI.API import StudentAPI, TrickerAPI
  13. from PyAPI.AI import Setting
  14. from PyAPI.Communication import Communication
  15. from PyAPI.State import State
  16. from PyAPI.Interface import ILogic, IGameTimer
  17. class Logic(ILogic):
  18. def __init__(self, playerID: int) -> None:
  19. # ID
  20. self.__playerID: int = playerID
  21. self.__playerGUIDs: List[int] = []
  22. # 通信
  23. self.__comm: Communication
  24. # 存储状态
  25. self.__currentState: State
  26. self.__bufferState: State
  27. # timer,用于实际运行AI
  28. self.__timer: IGameTimer
  29. # AI线程
  30. self.__threadAI: threading.Thread
  31. # 互斥锁
  32. self.__mtxState: threading.Lock = threading.Lock()
  33. # 条件变量
  34. self.__cvBuffer: threading.Condition = threading.Condition()
  35. self.__cvAI: threading.Condition = threading.Condition()
  36. # 保存缓冲区数
  37. self.__counterState: int
  38. self.__counterBuffer: int
  39. # 记录游戏状态
  40. self.__gameState: THUAI6.GameState = THUAI6.GameState.NullGameState
  41. # 是否应该执行player()
  42. self.__AILoop: bool = True
  43. # buffer是否更新完毕
  44. self.__bufferUpdated: bool = False
  45. # 是否应当启动AI
  46. self.__AIStart: bool = False
  47. # asynchronous为True时控制内容更新的变量
  48. self.__freshed: bool = False
  49. self.__logger: logging.Logger = logging.getLogger("logic")
  50. # IAPI统一可用的接口
  51. def GetTrickers(self) -> List[THUAI6.Tricker]:
  52. with self.__mtxState:
  53. self.__logger.debug("Called GetTrickers")
  54. return self.__currentState.trickers
  55. def GetStudents(self) -> List[THUAI6.Student]:
  56. with self.__mtxState:
  57. self.__logger.debug("Called GetStudents")
  58. return self.__currentState.students
  59. def GetProps(self) -> List[THUAI6.Prop]:
  60. with self.__mtxState:
  61. self.__logger.debug("Called GetProps")
  62. return self.__currentState.props
  63. def GetSelfInfo(self) -> Union[THUAI6.Student, THUAI6.Tricker]:
  64. with self.__mtxState:
  65. self.__logger.debug("Called GetSelfInfo")
  66. return self.__currentState.self
  67. def GetFullMap(self) -> List[List[THUAI6.PlaceType]]:
  68. with self.__mtxState:
  69. self.__logger.debug("Called GetFullMap")
  70. return self.__currentState.gameMap
  71. def GetPlaceType(self, x: int, y: int) -> THUAI6.PlaceType:
  72. with self.__mtxState:
  73. self.__logger.debug("Called GetPlaceType")
  74. return self.__currentState.gameMap[x][y]
  75. def IsDoorOpen(self, x: int, y: int) -> bool:
  76. with self.__mtxState:
  77. self.__logger.debug("Called IsDoorOpen")
  78. if (x, y) in self.__currentState.mapInfo.doorState:
  79. return self.__currentState.mapInfo.doorState[(x, y)]
  80. else:
  81. return False
  82. def GetClassroomProgess(self, x: int, y: int) -> int:
  83. with self.__mtxState:
  84. self.__logger.debug("Called GetClassroomProgess")
  85. if (x, y) in self.__currentState.mapInfo.classroomState:
  86. return self.__currentState.mapInfo.classroomState[(x, y)]
  87. else:
  88. return 0
  89. def GetChestProgress(self, x: int, y: int) -> int:
  90. with self.__mtxState:
  91. self.__logger.debug("Called GetChestProgress")
  92. if (x, y) in self.__currentState.mapInfo.chestState:
  93. return self.__currentState.mapInfo.chestState[(x, y)]
  94. else:
  95. return 0
  96. def GetGateProgress(self, x: int, y: int) -> int:
  97. with self.__mtxState:
  98. self.__logger.debug("Called GetGateProgress")
  99. if (x, y) in self.__currentState.mapInfo.gateState:
  100. return self.__currentState.mapInfo.gateState[(x, y)]
  101. else:
  102. return 0
  103. def GetGameInfo(self) -> THUAI6.GameInfo:
  104. with self.__mtxState:
  105. self.__logger.debug("Called GetGameInfo")
  106. return self.__currentState.gameInfo
  107. def Move(self, time: int, angle: float) -> bool:
  108. self.__logger.debug("Called Move")
  109. return self.__comm.Move(time, angle, self.__playerID)
  110. def PickProp(self, propType: THUAI6.PropType) -> bool:
  111. self.__logger.debug("Called PickProp")
  112. return self.__comm.PickProp(propType, self.__playerID)
  113. def UseProp(self) -> bool:
  114. self.__logger.debug("Called UseProp")
  115. return self.__comm.UseProp(self.__playerID)
  116. def UseSkill(self) -> bool:
  117. self.__logger.debug("Called UseSkill")
  118. return self.__comm.UseSkill(self.__playerID)
  119. def SendMessage(self, toID: int, message: str) -> bool:
  120. self.__logger.debug("Called SendMessage")
  121. return self.__comm.SendMessage(toID, message, self.__playerID)
  122. def HaveMessage(self) -> bool:
  123. self.__logger.debug("Called HaveMessage")
  124. return self.__comm.HaveMessage()
  125. def GetMessage(self) -> tuple[int, str]:
  126. self.__logger.debug("Called GetMessage")
  127. return self.__comm.GetMessage()
  128. def WaitThread(self) -> bool:
  129. self.__Update()
  130. return True
  131. def GetCounter(self) -> int:
  132. with self.__mtxState:
  133. return self.__counterState
  134. def GetPlayerGUIDs(self) -> List[int]:
  135. with self.__mtxState:
  136. return self.__playerGUIDs
  137. # IStudentAPI使用的接口
  138. def Graduate(self) -> bool:
  139. self.__logger.debug("Called Graduate")
  140. return self.__comm.Graduate(self.__playerID)
  141. def StartLearning(self) -> bool:
  142. self.__logger.debug("Called StartLearning")
  143. return self.__comm.StartLearning(self.__playerID)
  144. def StartTreatMate(self) -> bool:
  145. self.__logger.debug("Called StartTreatMate")
  146. return self.__comm.StartTreatMate(self.__playerID)
  147. def StartRescueMate(self) -> bool:
  148. self.__logger.debug("Called StartRescueMate")
  149. return self.__comm.StartRescueMate(self.__playerID)
  150. def Attack(self, angle: float) -> bool:
  151. self.__logger.debug("Called Trick")
  152. return self.__comm.Attack(angle, self.__playerID)
  153. def CloseDoor(self) -> bool:
  154. self.__logger.debug("Called CloseDoor")
  155. return self.__comm.CloseDoor(self.__playerID)
  156. def OpenDoor(self) -> bool:
  157. self.__logger.debug("Called OpenDoor")
  158. return self.__comm.OpenDoor(self.__playerID)
  159. def SkipWindow(self) -> bool:
  160. self.__logger.debug("Called SkipWindow")
  161. return self.__comm.SkipWindow(self.__playerID)
  162. def StartOpenGate(self) -> bool:
  163. self.__logger.debug("Called StartOpenGate")
  164. return self.__comm.StartOpenGate(self.__playerID)
  165. def StartOpenChest(self) -> bool:
  166. self.__logger.debug("Called StartOpenChest")
  167. return self.__comm.StartOpenChest(self.__playerID)
  168. def EndAllAction(self) -> bool:
  169. self.__logger.debug("Called EndAllAction")
  170. return self.__comm.EndAllAction(self.__playerID)
  171. # Logic内部逻辑
  172. def __TryConnection(self) -> bool:
  173. self.__logger.info("Try to connect to server...")
  174. return self.__comm.TryConnection(self.__playerID)
  175. def __ProcessMessage(self) -> None:
  176. def messageThread():
  177. self.__logger.info("Message thread start!")
  178. self.__comm.AddPlayer(self.__playerID)
  179. self.__logger.info("Join the player!")
  180. self.__comm.ReadMessage(self.__playerID)
  181. while self.__gameState != THUAI6.GameState.GameEnd:
  182. # 读取消息,无消息时此处阻塞
  183. clientMsg = self.__comm.GetMessage2Client()
  184. self.__logger.debug("Get message from server!")
  185. self.__gameState = Proto2THUAI6.gameStateDict[
  186. clientMsg.game_state]
  187. if self.__gameState == THUAI6.GameState.GameStart:
  188. # 读取玩家的GUID
  189. self.__logger.info("Game start!")
  190. self.__playerGUIDs.clear()
  191. for student in clientMsg.student_message:
  192. self.__playerGUIDs.append(student.guid)
  193. for tricker in clientMsg.tricker_message:
  194. self.__playerGUIDs.append(tricker.guid)
  195. self.__currentState.guids = self.__playerGUIDs
  196. self.__bufferState.guids = self.__playerGUIDs
  197. self.__LoadBuffer(clientMsg)
  198. self.__AILoop = True
  199. self.__UnBlockAI()
  200. elif self.__gameState == THUAI6.GameState.GameRunning:
  201. # 读取玩家的GUID
  202. self.__playerGUIDs.clear()
  203. for student in clientMsg.student_message:
  204. self.__playerGUIDs.append(student.guid)
  205. for tricker in clientMsg.tricker_message:
  206. self.__playerGUIDs.append(tricker.guid)
  207. self.__currentState.guids = self.__playerGUIDs
  208. self.__bufferState.guids = self.__playerGUIDs
  209. self.__LoadBuffer(clientMsg)
  210. else:
  211. self.__logger.error("Unknown GameState!")
  212. continue
  213. self.__AILoop = False
  214. with self.__cvBuffer:
  215. self.__bufferUpdated = True
  216. self.__counterBuffer = -1
  217. self.__cvBuffer.notify()
  218. self.__logger.info("Game End!")
  219. self.__logger.info("Message thread end!")
  220. threading.Thread(target=messageThread).start()
  221. def __LoadBuffer(self, message: Message2Clients.MessageToClient) -> None:
  222. with self.__cvBuffer:
  223. self.__bufferState.students.clear()
  224. self.__bufferState.trickers.clear()
  225. self.__bufferState.props.clear()
  226. self.__logger.debug("Buffer cleared!")
  227. self.__bufferState.map = Proto2THUAI6.Protobuf2THUAI6Map(
  228. message.map_message)
  229. if Setting.playerType() == THUAI6.PlayerType.StudentPlayer:
  230. for student in message.student_message:
  231. if student.player_id == self.__playerID:
  232. self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Student(
  233. student)
  234. self.__bufferState.students.append(
  235. Proto2THUAI6.Protobuf2THUAI6Student(student))
  236. self.__logger.debug("Add Student!")
  237. for tricker in message.tricker_message:
  238. if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, tricker.x, tricker.y, self.__bufferState.map):
  239. self.__bufferState.trickers.append(
  240. Proto2THUAI6.Protobuf2THUAI6Tricker(tricker))
  241. self.__logger.debug("Add Tricker!")
  242. else:
  243. for tricker in message.tricker_message:
  244. if tricker.player_id == self.__playerID:
  245. self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Tricker(
  246. tricker)
  247. self.__bufferState.trickers.append(
  248. Proto2THUAI6.Protobuf2THUAI6Tricker(tricker))
  249. self.__logger.debug("Add Tricker!")
  250. for student in message.student_message:
  251. if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, student.x, student.y, self.__bufferState.map):
  252. self.__bufferState.students.append(
  253. Proto2THUAI6.Protobuf2THUAI6Student(student))
  254. self.__logger.debug("Add Student!")
  255. for prop in message.prop_message:
  256. self.__bufferState.props.append(
  257. Proto2THUAI6.Protobuf2THUAI6Prop(prop))
  258. self.__logger.debug("Add Prop!")
  259. if Setting.asynchronous():
  260. with self.__mtxState:
  261. self.__currentState, self.__bufferState = self.__bufferState, self.__currentState
  262. self.__logger.info("Update state!")
  263. self.__freshed = True
  264. else:
  265. self.__bufferUpdated = True
  266. self.__counterBuffer += 1
  267. self.__cvBuffer.notify()
  268. def __UnBlockAI(self) -> None:
  269. with self.__cvAI:
  270. self.__AIStart = True
  271. self.__cvAI.notify()
  272. def __Update(self) -> None:
  273. if not Setting.asynchronous():
  274. with self.__cvBuffer:
  275. self.__cvBuffer.wait_for(lambda: self.__bufferUpdated)
  276. self.__bufferState, self.__currentState = self.__currentState, self.__bufferState
  277. self.__bufferUpdated = False
  278. self.__counterState = self.__counterBuffer
  279. self.__logger.info("Update state!")
  280. def __Wait(self) -> None:
  281. self.__freshed = False
  282. with self.__cvBuffer:
  283. self.__cvBuffer.wait_for(lambda: self.__freshed)
  284. def Main(self, createAI: Callable, IP: str, port: str, file: bool, screen: bool, warnOnly: bool) -> None:
  285. # 建立日志组件
  286. self.__logger.setLevel(logging.DEBUG)
  287. formatter = logging.Formatter(
  288. "[%(name)s] [%(asctime)s] [%(levelname)s] %(message)s", "%H:%M:%S.%e")
  289. # 确保文件存在
  290. if not os.path.exists(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"):
  291. os.makedirs(os.path.dirname(os.path.dirname(
  292. os.path.realpath(__file__))) + "/logs")
  293. fileHandler = logging.FileHandler(os.path.dirname(
  294. os.path.dirname(os.path.realpath(__file__))) + "/logs/logic-log.txt")
  295. screenHandler = logging.StreamHandler()
  296. if file:
  297. fileHandler.setLevel(logging.DEBUG)
  298. fileHandler.setFormatter(formatter)
  299. self.__logger.addHandler(fileHandler)
  300. if screen:
  301. if warnOnly:
  302. screenHandler.setLevel(logging.WARNING)
  303. else:
  304. screenHandler.setLevel(logging.INFO)
  305. screenHandler.setFormatter(formatter)
  306. self.__logger.addHandler(screenHandler)
  307. self.__logger.info("*********Basic Info*********")
  308. self.__logger.info("asynchronous: %s", Setting.asynchronous())
  309. self.__logger.info("server: %s:%s", IP, port)
  310. self.__logger.info("playerID: %s", self.__playerID)
  311. self.__logger.info("player type: %s", Setting.playerType().name)
  312. self.__logger.info("****************************")
  313. # 建立通信组件
  314. self.__comm = Communication(IP, port)
  315. # 构造timer
  316. if Setting.playerType() == THUAI6.PlayerType.StudentPlayer:
  317. if not file and not screen:
  318. self.__timer = StudentAPI(self)
  319. else:
  320. self.__timer = StudentDebugAPI(
  321. self, file, screen, warnOnly, self.__playerID)
  322. elif Setting.playerType() == THUAI6.PlayerType.TrickerPlayer:
  323. if not file and not screen:
  324. self.__timer = TrickerAPI(self)
  325. else:
  326. self.__timer = TrickerDebugAPI(
  327. self, file, screen, warnOnly, self.__playerID)
  328. # 构建AI线程
  329. def AIThread():
  330. with self.__cvAI:
  331. self.__cvAI.wait_for(lambda: self.__AIStart)
  332. ai = createAI()
  333. while self.__AILoop:
  334. if Setting.asynchronous():
  335. self.__Wait()
  336. self.__timer.StartTimer()
  337. self.__timer.Play(ai)
  338. self.__timer.EndTimer()
  339. else:
  340. self.__Update()
  341. self.__timer.StartTimer()
  342. self.__timer.Play(ai)
  343. self.__timer.EndTimer()
  344. if self.__TryConnection():
  345. self.__logger.info(
  346. "Connect to the server successfully, AI thread will be started.")
  347. self.__threadAI = threading.Thread(target=AIThread)
  348. self.__threadAI.start()
  349. self.__ProcessMessage()
  350. self.__logger.info("Join the AI thread.")
  351. self.__threadAI.join()
  352. else:
  353. self.__AILoop = False
  354. self.__logger.error("Failed to connect to the server.")
  355. return