pyscreeps-arena 0.5.6.5__tar.gz → 0.5.7.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.
Files changed (33) hide show
  1. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/PKG-INFO +1 -1
  2. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/__init__.py +12 -2
  3. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/compiler.py +197 -16
  4. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/core/const.py +1 -1
  5. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/localization.py +4 -0
  6. pyscreeps_arena-0.5.7.0/pyscreeps_arena/project.7z +0 -0
  7. pyscreeps_arena-0.5.7.0/pyscreeps_arena/ui/creeplogic_edit.py +14 -0
  8. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/ui/project_ui.py +1 -0
  9. pyscreeps_arena-0.5.7.0/pyscreeps_arena/ui/qcreeplogic/__init__.py +3 -0
  10. pyscreeps_arena-0.5.7.0/pyscreeps_arena/ui/qcreeplogic/model.py +72 -0
  11. pyscreeps_arena-0.5.7.0/pyscreeps_arena/ui/qcreeplogic/qcreeplogic.py +770 -0
  12. pyscreeps_arena-0.5.7.0/pyscreeps_arena/ui/qrecipe/__init__.py +1 -0
  13. pyscreeps_arena-0.5.7.0/pyscreeps_arena/ui/qrecipe/model.py +434 -0
  14. pyscreeps_arena-0.5.7.0/pyscreeps_arena/ui/qrecipe/qrecipe.py +914 -0
  15. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena.egg-info/PKG-INFO +1 -1
  16. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena.egg-info/SOURCES.txt +8 -1
  17. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/setup.py +1 -1
  18. pyscreeps_arena-0.5.6.5/pyscreeps_arena/project.7z +0 -0
  19. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/build.py +0 -0
  20. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/core/__init__.py +0 -0
  21. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/core/basic.py +0 -0
  22. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/core/config.py +0 -0
  23. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/core/core.py +0 -0
  24. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/core/main.py +0 -0
  25. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/core/utils.py +0 -0
  26. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/ui/P2PY.py +0 -0
  27. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/ui/__init__.py +0 -0
  28. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena/ui/rs_icon.py +0 -0
  29. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena.egg-info/dependency_links.txt +0 -0
  30. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena.egg-info/entry_points.txt +0 -0
  31. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena.egg-info/requires.txt +0 -0
  32. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/pyscreeps_arena.egg-info/top_level.txt +0 -0
  33. {pyscreeps_arena-0.5.6.5 → pyscreeps_arena-0.5.7.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyscreeps-arena
3
- Version: 0.5.6.5
3
+ Version: 0.5.7.0
4
4
  Summary: Python api|interface to play game: Screeps: Arena.
5
5
  Author-email: 2229066748@qq.com
6
6
  Maintainer: Eagle'sBaby
@@ -35,8 +35,18 @@ def CMD_OpenUI():
35
35
 
36
36
  """
37
37
  try:
38
- from pyscreeps_arena.ui.project_ui import run_project_creator
39
- run_project_creator()
38
+ # 检查是否带 -c
39
+ if len(sys.argv) > 1 and sys.argv[1] == '-c':
40
+ from pyscreeps_arena.ui.creeplogic_edit import run_creeplogic_edit
41
+ run_creeplogic_edit()
42
+ elif len(sys.argv) > 1 and sys.argv[1] == '-e':
43
+ from pyscreeps_arena.ui.creeplogic_edit import run_creeplogic_edit
44
+ from pyscreeps_arena.core import config
45
+ config.language = 'en'
46
+ run_creeplogic_edit()
47
+ else:
48
+ from pyscreeps_arena.ui.project_ui import run_project_creator
49
+ run_project_creator()
40
50
  except ImportError as e:
41
51
  print(f"错误: 无法导入UI模块 - {e}")
42
52
  print("请确保已安装PyQt6: pip install PyQt6")
@@ -9,6 +9,7 @@ import chardet
9
9
  import subprocess
10
10
  import pyperclip
11
11
  from colorama import Fore
12
+ from typing import List, Optional, Tuple, Union
12
13
 
13
14
  WAIT = Fore.YELLOW + ">>>" + Fore.RESET
14
15
  GREEN = Fore.GREEN + "{}" + Fore.RESET
@@ -92,18 +93,18 @@ export var loop = function () {
92
93
  timeLine = get.cpu_us();
93
94
  know.handle();
94
95
  knowCost = get.cpu_us() - timeLine;
95
- if (know.now === 1) {
96
- std.show_welcome();
97
- init (know);
98
-
99
- }
100
-
96
+
101
97
  timeLine = get.cpu_us();
102
98
  monitor.handle();
103
99
  monitorCost = get.cpu_us() - timeLine;
104
100
  for (const creep of know.creeps){
105
101
  creep.handle();
106
102
  }
103
+ if (know.now === 1) {
104
+ std.show_welcome();
105
+ init (know);
106
+
107
+ }
107
108
  step (know);
108
109
  timeLine = get.cpu_us();
109
110
  if (get._SCH_FLAG) sch.handle();
@@ -229,12 +230,16 @@ class Compiler_Utils(Compiler_Const):
229
230
  return f.read()
230
231
  except UnicodeDecodeError:
231
232
  # 如果使用检测到的编码读取失败,尝试使用chardet检测编码
232
- with open(fpath, 'rb') as f: # 以二进制模式打开文件
233
- raw_data = f.read() # 读取文件的原始数据
234
- result = chardet.detect(raw_data) # 使用chardet检测编码
235
- encoding = result['encoding'] # 获取检测到的编码
236
- with open(fpath, 'r', encoding=encoding) as f: # 使用检测到的编码打开文件
237
- return f.read()
233
+ try:
234
+ with open(fpath, 'rb') as f: # 以二进制模式打开文件
235
+ raw_data = f.read() # 读取文件的原始数据
236
+ result = chardet.detect(raw_data) # 使用chardet检测编码
237
+ encoding = result['encoding'] # 获取检测到的编码
238
+ with open(fpath, 'r', encoding=encoding) as f: # 使用检测到的编码打开文件
239
+ return f.read()
240
+ except UnicodeDecodeError as e:
241
+ core.error('Compiler_Utils.auto_read', core.lformat(LOC_FILE_READ_FAILED, [fpath, "UnicodeError", e]), end='', head='\n', ln=config.language)
242
+ quit(-1)
238
243
 
239
244
  def copy_to(self) -> list:
240
245
  """
@@ -685,6 +690,167 @@ class Compiler_Utils(Compiler_Const):
685
690
  code = re.sub(r"'''[^']*'''", '', code)
686
691
  return code
687
692
 
693
+ @classmethod
694
+ def _collect_logical_line(cls, lines: List[str], start_idx: int) -> Tuple[str, int]:
695
+ """收集从start_idx开始的逻辑行(处理多行语句,直到遇到:结尾)"""
696
+ if start_idx >= len(lines):
697
+ return "", start_idx
698
+
699
+ parts = [lines[start_idx].rstrip()]
700
+ i = start_idx
701
+
702
+ # 持续收集直到找到以:结尾的行
703
+ while i < len(lines) and not parts[-1].endswith(':'):
704
+ i += 1
705
+ if i < len(lines):
706
+ parts.append(lines[i].rstrip())
707
+
708
+ return " ".join(parts), i
709
+
710
+ @classmethod
711
+ def _convert_block(cls, lines: List[str], match_counter: Optional[List[int]] = None) -> List[str]:
712
+ if match_counter is None:
713
+ match_counter = [0]
714
+
715
+ result = []
716
+ i = 0
717
+
718
+ while i < len(lines):
719
+ line = lines[i].rstrip()
720
+
721
+ # 检测match语句(支持多行)
722
+ if re.match(r'^\s*match\s+', line):
723
+ full_match, end_idx = cls._collect_logical_line(lines, i)
724
+
725
+ match_stmt = re.match(r'^(\s*)match\s+(.+?)\s*:', full_match)
726
+ if not match_stmt:
727
+ result.append(line)
728
+ i += 1
729
+ continue
730
+
731
+ indent = match_stmt.group(1)
732
+ subject = match_stmt.group(2).strip()
733
+ i = end_idx + 1 # 跳过match语句
734
+
735
+ # 生成临时变量
736
+ var_name = f"__MATCH_{match_counter[0]}__"
737
+ match_counter[0] += 1
738
+ result.append(f"{indent}{var_name} = {subject}")
739
+
740
+ # 解析case语句
741
+ cases: List[Tuple[str, List[str]]] = []
742
+ case_indent = None
743
+
744
+ while i < len(lines):
745
+ case_line = lines[i].rstrip()
746
+
747
+ # 缩进检查 - case必须比match缩进更多
748
+ if not case_line.startswith(indent + ' ') and case_line.strip():
749
+ if re.match(r'^\s*case\s+', case_line):
750
+ raise MatchCaseError(f"第 {i + 1} 行: case 缩进必须大于 match")
751
+ break
752
+
753
+ # 检测case语句(不再使用_collect_logical_line,而是单独处理每一行)
754
+ case_match = re.match(r'^(\s+)case\s+(.+?)\s*:', case_line)
755
+ if case_match:
756
+ curr_case_indent = case_match.group(1)
757
+ case_val = case_match.group(2).strip()
758
+
759
+ # 验证缩进 - 允许不同的case缩进(用于嵌套)
760
+ if len(curr_case_indent) <= len(indent):
761
+ raise MatchCaseError(f"第 {i + 1} 行: case 缩进必须大于 match")
762
+
763
+ # 不再强制要求所有case缩进一致,允许嵌套情况下的不同缩进
764
+ if case_indent is None:
765
+ case_indent = curr_case_indent
766
+
767
+ # 提取内联代码(如果有)
768
+ inline_code = ""
769
+ if ':' in case_line:
770
+ after_colon = case_line.split(':', 1)[1].strip()
771
+ if after_colon:
772
+ inline_code = after_colon
773
+
774
+ i += 1
775
+
776
+ # 收集case块
777
+ block_lines = []
778
+ if inline_code:
779
+ block_lines.append(f"{curr_case_indent} {inline_code}")
780
+
781
+ while i < len(lines):
782
+ block_line = lines[i].rstrip()
783
+ if not block_line.strip():
784
+ block_lines.append(block_line)
785
+ i += 1
786
+ continue
787
+
788
+ # 检查是否是下一个case或者缩进回到当前match级别
789
+ if re.match(r'^\s*case\s+', block_line):
790
+ # 检查这个case是否属于当前match还是父级match
791
+ next_case_indent = re.match(r'^\s*', block_line).group(0)
792
+ if len(next_case_indent) <= len(indent):
793
+ # 属于父级match,退出当前match的处理
794
+ break
795
+ # 仍然属于当前match,继续收集
796
+ if block_line.startswith(curr_case_indent + ' '):
797
+ block_lines.append(block_line)
798
+ i += 1
799
+ continue
800
+ else:
801
+ break
802
+
803
+ if block_line.startswith(indent) and not block_line.startswith(curr_case_indent):
804
+ break
805
+
806
+ if block_line.startswith(curr_case_indent + ' '):
807
+ block_lines.append(block_line)
808
+ i += 1
809
+ continue
810
+
811
+ break
812
+
813
+ cases.append((case_val, block_lines))
814
+ else:
815
+ break
816
+
817
+ # 验证case
818
+ seen = set()
819
+ for idx, (val, _) in enumerate(cases):
820
+ if val == '_':
821
+ if idx != len(cases) - 1:
822
+ raise MatchCaseError(f"第 {i + 1} 行附近: case _ 必须在最后")
823
+ else:
824
+ if val in seen:
825
+ raise MatchCaseError(f"第 {i + 1} 行附近: 重复的 case 值 '{val}'")
826
+ seen.add(val)
827
+
828
+ # 生成if/elif/else
829
+ for idx, (case_val, blk_lines) in enumerate(cases):
830
+ keyword = "else" if case_val == '_' else ("if" if idx == 0 else "elif")
831
+ if keyword == "else":
832
+ result.append(f"{indent}else:")
833
+ else:
834
+ result.append(f"{indent}{keyword} {var_name} == {case_val}:")
835
+
836
+ if blk_lines:
837
+ # 递归处理block_lines,以支持嵌套match
838
+ converted_blk_lines = cls._convert_block(blk_lines, match_counter)
839
+ result.extend(converted_blk_lines)
840
+
841
+ continue
842
+
843
+ result.append(line)
844
+ i += 1
845
+
846
+ return result
847
+
848
+ @classmethod
849
+ def convert_match_to_if(cls, code: str) -> str:
850
+ lines = code.split('\n')
851
+ converted_lines = cls._convert_block(lines, [0])
852
+ return '\n'.join(converted_lines)
853
+
688
854
 
689
855
  class CompilerBase(Compiler_Utils):
690
856
 
@@ -827,10 +993,11 @@ class Compiler(CompilerBase):
827
993
  else:
828
994
  _pre_sort_[fname] = 65535
829
995
 
830
- # ------------------------------------ 自定义:调用process_mate_code ------------------------------------ #
996
+ # ------------------------------------ 自定义:mate & match ------------------------------------ #
831
997
  for fpath in py_fpath:
832
998
  content = self.auto_read(fpath)
833
999
  content = self.process_mate_code(content) # 调用process_mate_code
1000
+ content = self.convert_match_to_if(content) # 调用convert_match_to_if
834
1001
  with open(fpath, 'w', encoding='utf-8') as f:
835
1002
  f.write(content)
836
1003
 
@@ -1216,6 +1383,20 @@ class Compiler(CompilerBase):
1216
1383
 
1217
1384
 
1218
1385
  if __name__ == '__main__':
1219
- compiler = Compiler('src', 'library', 'build')
1220
- compiler.compile()
1221
- compiler.clean()
1386
+ # compiler = Compiler('src', 'library', 'build')
1387
+ # compiler.compile()
1388
+ # compiler.clean()
1389
+ test = """
1390
+
1391
+ def patrolling(self, c: Creep):
1392
+ e = self.center.nearest(k.civilian.enemies, 5)
1393
+ if e:
1394
+ match c.test(e,
1395
+ int(c.hpPer <= 0.9) * 2,
1396
+ int(c.info.melee) * -3
1397
+ ):
1398
+ case True: c.move(e, SWAMP_MOTION)
1399
+ case False: c.move(self.bpos, SWAMP_MOTION)
1400
+
1401
+ """
1402
+ print(f"res=\n{Compiler.convert_match_to_if(test)}")
@@ -9,7 +9,7 @@
9
9
  #
10
10
  import re
11
11
 
12
- VERSION = "0.5.6.5"
12
+ VERSION = "0.5.7.0"
13
13
  AUTHOR = "●ω<🤍♪"
14
14
  STEAM_ID = "1029562896"
15
15
  GITHUB_NAME = "EagleBaby"
@@ -17,6 +17,10 @@ LOC_FILE_NOT_EXISTS = {
17
17
  'en': "{} File not exists: {}. You can ignore if it's a not used file.",
18
18
  'cn': "{} 文件不存在: {}. 如果它是一个未使用的文件,您可以忽略它。",
19
19
  }
20
+ LOC_FILE_READ_FAILED = {
21
+ 'en': "Failed to read file: {}({})\nDetails:\n{}",
22
+ 'cn': "读取文件失败: {}({})\n详细信息:\n{}",
23
+ }
20
24
  LOC_PREPROCESSING = {
21
25
  'en': "Preprocessing ...",
22
26
  'cn': "预处理中 ...",
@@ -0,0 +1,14 @@
1
+ import sys
2
+ from PyQt6.QtWidgets import QApplication
3
+ from pyscreeps_arena.ui.qcreeplogic import QPSACreepLogic
4
+
5
+
6
+
7
+ def run_creeplogic_edit():
8
+ app = QApplication(sys.argv)
9
+ window = QPSACreepLogic()
10
+ window.show()
11
+ sys.exit(app.exec())
12
+
13
+ if __name__ == '__main__':
14
+ run_creeplogic_edit()
@@ -15,6 +15,7 @@ from pyscreeps_arena.core import const
15
15
  from pyscreeps_arena.ui.rs_icon import get_pixmap
16
16
 
17
17
 
18
+
18
19
  class ProjectCreatorUI(QMainWindow):
19
20
  def __init__(self):
20
21
  super().__init__()
@@ -0,0 +1,3 @@
1
+ from pyscreeps_arena.ui.qcreeplogic.qcreeplogic import QPSACreepLogic
2
+
3
+
@@ -0,0 +1,72 @@
1
+ from typing import List, Optional, Union
2
+ from pyscreeps_arena.ui.qrecipe.model import PartsVector
3
+
4
+ class NamedRecipe:
5
+ def __init__(self, name: str, recipe: List[str]):
6
+ self.name = name
7
+ self.recipe = recipe
8
+
9
+ class CreepLogicSettings:
10
+ """
11
+ 爬虫逻辑设置类,用于定义爬虫的基本属性和行为。
12
+ """
13
+
14
+ def __init__(self):
15
+ # ---------------------- 爬虫本体 | Creep --------------------------- #
16
+ self.draw: bool = False # 是否绘制爬虫信息
17
+ self.layer: int = 10 # 爬虫绘制图层,默认为10
18
+
19
+ # ---------------------- 流程与结构 | Flow & Struct --------------------------- #
20
+ self.link: Union[List[str], str, None] = None # 爬虫逻辑链接
21
+ self.once: bool = True # 是否禁用死亡后重生
22
+
23
+ # ---------------------- 孵化选项 | Spawning --------------------------- #
24
+ self.spawnable = True # 是否可以孵化标志
25
+ self.recipe: Union[List[str], NamedRecipe] = ["MOVE"] # 爬虫配方,默认为["MOVE"]
26
+ self.optimise: bool = True # 自动优化配方标志
27
+ self.extension: bool = True # 使用extension标志
28
+ self.direction: Optional[int] = None # 出生方向
29
+
30
+ # ---------------------- 基本信息 | Basic Info --------------------------- #
31
+ self.name: str = "" # 爬虫名称
32
+
33
+ def to_dict(self) -> dict:
34
+ """
35
+ 将设置转换为字典格式
36
+ """
37
+ return {
38
+ "name": self.name,
39
+ "draw": self.draw,
40
+ "layer": self.layer,
41
+ "link": self.link,
42
+ "once": self.once,
43
+ "spawnable": self.spawnable,
44
+ "recipe": self.recipe if isinstance(self.recipe, list) else self.recipe.name,
45
+ "optimise": self.optimise,
46
+ "extension": self.extension,
47
+ "direction": self.direction
48
+ }
49
+
50
+ @classmethod
51
+ def from_dict(cls, data: dict) -> "CreepLogicSettings":
52
+ """
53
+ 从字典格式创建设置对象
54
+ """
55
+ settings = cls()
56
+ settings.name = data.get("name", "")
57
+ settings.draw = data.get("draw", False)
58
+ settings.layer = data.get("layer", 10)
59
+ settings.link = data.get("link", None)
60
+ settings.once = data.get("once", True)
61
+ settings.spawnable = data.get("spawnable", True)
62
+ settings.recipe = data.get("recipe", ["MOVE"])
63
+ settings.optimise = data.get("optimise", True)
64
+ settings.extension = data.get("extension", True)
65
+ settings.direction = data.get("direction", None)
66
+ return settings
67
+
68
+ def reset(self):
69
+ """
70
+ 重置所有设置为默认值
71
+ """
72
+ self.__init__()