codingpirates-maze 0.1.0__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.
Files changed (539) hide show
  1. codingpirates_maze-0.1.0.dist-info/METADATA +105 -0
  2. codingpirates_maze-0.1.0.dist-info/RECORD +539 -0
  3. codingpirates_maze-0.1.0.dist-info/WHEEL +4 -0
  4. codingpirates_maze-0.1.0.dist-info/licenses/LICENSE +201 -0
  5. importer/code_blocks.py +339 -0
  6. importer/convert_level.py +150 -0
  7. importer/create_course.py +278 -0
  8. importer/lesson.md.j2 +3 -0
  9. importer/maze.py.j2 +20 -0
  10. importer/test.py +24 -0
  11. importer/test_all.py +24 -0
  12. maze/__init__.py +9 -0
  13. maze/bee.py +184 -0
  14. maze/beeshape.py +115 -0
  15. maze/birds.py +69 -0
  16. maze/collector.py +106 -0
  17. maze/farmer.py +95 -0
  18. maze/harvester.py +141 -0
  19. maze/images/bee/background.png +0 -0
  20. maze/images/bee/cloud.gif +0 -0
  21. maze/images/bee/honeycomb.gif +0 -0
  22. maze/images/bee/path.gif +0 -0
  23. maze/images/bee/purple_flower.gif +0 -0
  24. maze/images/bee/red_flower.gif +0 -0
  25. maze/images/birds/.gitignore +2 -0
  26. maze/images/birds/background.png +0 -0
  27. maze/images/birds/monster.gif +0 -0
  28. maze/images/birds/path.gif +0 -0
  29. maze/images/birds/tnt.gif +0 -0
  30. maze/images/birds/wall_1.gif +0 -0
  31. maze/images/birds/wall_2.gif +0 -0
  32. maze/images/birds/wall_3.gif +0 -0
  33. maze/images/birds/wall_4.gif +0 -0
  34. maze/images/birds/wall_5.gif +0 -0
  35. maze/images/collector/.gitignore +1 -0
  36. maze/images/collector/crystal.gif +0 -0
  37. maze/images/collector/floor_1.gif +0 -0
  38. maze/images/collector/floor_2.gif +0 -0
  39. maze/images/collector/floor_3.gif +0 -0
  40. maze/images/collector/floor_4.gif +0 -0
  41. maze/images/collector/wall_1.gif +0 -0
  42. maze/images/collector/wall_2.gif +0 -0
  43. maze/images/farmer/background.png +0 -0
  44. maze/images/farmer/corn.gif +0 -0
  45. maze/images/farmer/hole.gif +0 -0
  46. maze/images/farmer/lettuce.gif +0 -0
  47. maze/images/farmer/path.gif +0 -0
  48. maze/images/farmer/pile.gif +0 -0
  49. maze/images/farmer/pumpkin.gif +0 -0
  50. maze/images/farmer/sprout.gif +0 -0
  51. maze/images/pvz/background.gif +0 -0
  52. maze/images/pvz/flower.gif +0 -0
  53. maze/images/pvz/obstacle.gif +0 -0
  54. maze/images/pvz/path.gif +0 -0
  55. maze/images/pvz/pumpkin.gif +0 -0
  56. maze/levels/CourseD_2022LessonExtra14a_2023.json +46 -0
  57. maze/levels/CourseD_2022LessonExtra14a_2024.json +46 -0
  58. maze/levels/CourseD_2022LessonExtra14a_2025.json +46 -0
  59. maze/levels/CourseD_2022LessonExtra15a_2023.json +46 -0
  60. maze/levels/CourseD_2022LessonExtra15a_2024.json +46 -0
  61. maze/levels/CourseD_2022LessonExtra15a_2025.json +46 -0
  62. maze/levels/CourseD_2022LessonExtra16a_2023.json +46 -0
  63. maze/levels/CourseD_2022LessonExtra16a_2024.json +46 -0
  64. maze/levels/CourseD_2022LessonExtra16a_2025.json +46 -0
  65. maze/levels/CourseD_2022_LessonExtra14b_2023.json +47 -0
  66. maze/levels/CourseD_2022_LessonExtra14b_2024.json +47 -0
  67. maze/levels/CourseD_2022_LessonExtra14b_2025.json +47 -0
  68. maze/levels/CourseD_2022_LessonExtra15b_2023.json +47 -0
  69. maze/levels/CourseD_2022_LessonExtra15b_2024.json +47 -0
  70. maze/levels/CourseD_2022_LessonExtra15b_2025.json +47 -0
  71. maze/levels/CourseD_2022_LessonExtra16b_2023.json +47 -0
  72. maze/levels/CourseD_2022_LessonExtra16b_2024.json +47 -0
  73. maze/levels/CourseD_2022_LessonExtra16b_2025.json +47 -0
  74. maze/levels/courseA_harvester_loops10_2024.json +51 -0
  75. maze/levels/courseA_harvester_loops11_2024.json +51 -0
  76. maze/levels/courseA_harvester_loops1_2024.json +50 -0
  77. maze/levels/courseA_harvester_loops2_2024.json +54 -0
  78. maze/levels/courseA_harvester_loops3_2024.json +57 -0
  79. maze/levels/courseA_harvester_loops4_2024.json +52 -0
  80. maze/levels/courseA_harvester_loops5_2024.json +53 -0
  81. maze/levels/courseA_harvester_loops5a_2024.json +53 -0
  82. maze/levels/courseA_harvester_loops5b_2024.json +52 -0
  83. maze/levels/courseA_harvester_loops6_2024.json +55 -0
  84. maze/levels/courseA_harvester_loops7_2024.json +53 -0
  85. maze/levels/courseA_harvester_loops9_2024.json +51 -0
  86. maze/levels/courseA_harvester_seq10_2024.json +50 -0
  87. maze/levels/courseA_harvester_seq11_2024.json +50 -0
  88. maze/levels/courseA_harvester_seq12_2024.json +50 -0
  89. maze/levels/courseA_harvester_seq13_2024.json +50 -0
  90. maze/levels/courseA_harvester_seq1_2024.json +49 -0
  91. maze/levels/courseA_harvester_seq2_2024.json +54 -0
  92. maze/levels/courseA_harvester_seq3_2024.json +54 -0
  93. maze/levels/courseA_harvester_seq4_2024.json +51 -0
  94. maze/levels/courseA_harvester_seq5_2024.json +51 -0
  95. maze/levels/courseA_harvester_seq6_2024.json +50 -0
  96. maze/levels/courseA_harvester_seq7_2024.json +51 -0
  97. maze/levels/courseA_harvester_seq8_2024.json +51 -0
  98. maze/levels/courseA_harvester_seq9_2024.json +51 -0
  99. maze/levels/courseA_maze_ramp2_2024.json +53 -0
  100. maze/levels/courseA_maze_ramp3a_2024.json +53 -0
  101. maze/levels/courseA_maze_ramp3b_2024.json +50 -0
  102. maze/levels/courseA_maze_ramp4a_2024.json +48 -0
  103. maze/levels/courseA_maze_ramp5a_2024.json +47 -0
  104. maze/levels/courseA_maze_seq10_2024.json +48 -0
  105. maze/levels/courseA_maze_seq11_2024.json +47 -0
  106. maze/levels/courseA_maze_seq12_2024.json +47 -0
  107. maze/levels/courseA_maze_seq13_2024.json +49 -0
  108. maze/levels/courseA_maze_seq1_2024.json +52 -0
  109. maze/levels/courseA_maze_seq5_2024.json +47 -0
  110. maze/levels/courseA_maze_seq5a_2024.json +47 -0
  111. maze/levels/courseA_maze_seq6_2024.json +47 -0
  112. maze/levels/courseA_maze_seq8_2024.json +47 -0
  113. maze/levels/courseA_maze_seq_challenge1_2024.json +45 -0
  114. maze/levels/courseA_maze_seq_challenge2_2024.json +50 -0
  115. maze/levels/courseB_collector_loops10_2024.json +53 -0
  116. maze/levels/courseB_collector_loops11_2024.json +51 -0
  117. maze/levels/courseB_collector_loops1_2024.json +53 -0
  118. maze/levels/courseB_collector_loops2_2024.json +53 -0
  119. maze/levels/courseB_collector_loops3_2024.json +57 -0
  120. maze/levels/courseB_collector_loops4_2024.json +57 -0
  121. maze/levels/courseB_collector_loops5_2024.json +54 -0
  122. maze/levels/courseB_collector_loops6_2024.json +54 -0
  123. maze/levels/courseB_collector_loops7_2024.json +53 -0
  124. maze/levels/courseB_collector_loops8_2024.json +54 -0
  125. maze/levels/courseB_collector_loops9_2024.json +54 -0
  126. maze/levels/courseB_collector_loops_challenge1_2024.json +54 -0
  127. maze/levels/courseB_collector_loops_challenge2a_2024.json +51 -0
  128. maze/levels/courseC_collector_loops_challenge2_2024.json +53 -0
  129. maze/levels/courseC_collector_prog1_2023.json +52 -0
  130. maze/levels/courseC_collector_prog1_2024.json +52 -0
  131. maze/levels/courseC_collector_prog2_2023.json +53 -0
  132. maze/levels/courseC_collector_prog2_2024.json +53 -0
  133. maze/levels/courseC_collector_prog3_2023.json +49 -0
  134. maze/levels/courseC_collector_prog3_2024.json +49 -0
  135. maze/levels/courseC_collector_prog4_2023.json +50 -0
  136. maze/levels/courseC_collector_prog4_2024.json +50 -0
  137. maze/levels/courseC_collector_prog5_2023.json +50 -0
  138. maze/levels/courseC_collector_prog5_2024.json +50 -0
  139. maze/levels/courseC_collector_prog6_2023.json +50 -0
  140. maze/levels/courseC_collector_prog6_2024.json +50 -0
  141. maze/levels/courseC_collector_prog7_2023.json +51 -0
  142. maze/levels/courseC_collector_prog7_2024.json +51 -0
  143. maze/levels/courseC_collector_prog8_2023.json +52 -0
  144. maze/levels/courseC_collector_prog8_2024.json +52 -0
  145. maze/levels/courseC_collector_prog9_2023.json +52 -0
  146. maze/levels/courseC_collector_prog9_2024.json +52 -0
  147. maze/levels/courseC_collector_prog_challenge1_2023.json +51 -0
  148. maze/levels/courseC_collector_prog_challenge1_2024.json +51 -0
  149. maze/levels/courseC_collector_prog_challenge2_2023.json +51 -0
  150. maze/levels/courseC_collector_prog_challenge2_2024.json +51 -0
  151. maze/levels/courseC_harvester_loops10_2024.json +50 -0
  152. maze/levels/courseC_harvester_loops11_2024.json +50 -0
  153. maze/levels/courseC_harvester_loops11_predict1_2024.json +47 -0
  154. maze/levels/courseC_harvester_loops1_2024.json +52 -0
  155. maze/levels/courseC_harvester_loops2_2024.json +50 -0
  156. maze/levels/courseC_harvester_loops3_2024.json +50 -0
  157. maze/levels/courseC_harvester_loops4_2024.json +50 -0
  158. maze/levels/courseC_harvester_loops5_2024.json +50 -0
  159. maze/levels/courseC_harvester_loops6_2024.json +50 -0
  160. maze/levels/courseC_harvester_loops7_2024.json +50 -0
  161. maze/levels/courseC_harvester_loops8_2024.json +49 -0
  162. maze/levels/courseC_harvester_loops9_2024.json +50 -0
  163. maze/levels/courseC_harvester_loops_challenge1a_2024.json +50 -0
  164. maze/levels/courseC_harvester_loops_challenge2_2024.json +48 -0
  165. maze/levels/courseC_maze_debugging1.json +47 -0
  166. maze/levels/courseC_maze_debugging2.json +44 -0
  167. maze/levels/courseC_maze_debugging3.json +46 -0
  168. maze/levels/courseC_maze_debugging4.json +44 -0
  169. maze/levels/courseC_maze_debugging5.json +46 -0
  170. maze/levels/courseC_maze_debugging6.json +44 -0
  171. maze/levels/courseC_maze_debugging7.json +45 -0
  172. maze/levels/courseC_maze_debugging8_predict1.json +43 -0
  173. maze/levels/courseC_maze_debugging9.json +44 -0
  174. maze/levels/courseC_maze_loops_challenge1a_2024.json +47 -0
  175. maze/levels/courseC_maze_programming1_2023.json +50 -0
  176. maze/levels/courseC_maze_programming1_2024.json +50 -0
  177. maze/levels/courseC_maze_programming2_2023.json +53 -0
  178. maze/levels/courseC_maze_programming2_2024.json +53 -0
  179. maze/levels/courseC_maze_programming3_2023.json +47 -0
  180. maze/levels/courseC_maze_programming3_2024.json +47 -0
  181. maze/levels/courseC_maze_programming4_2023.json +46 -0
  182. maze/levels/courseC_maze_programming4_2024.json +46 -0
  183. maze/levels/courseC_maze_programming5_2023.json +46 -0
  184. maze/levels/courseC_maze_programming5_2024.json +46 -0
  185. maze/levels/courseC_maze_programming6_2023.json +50 -0
  186. maze/levels/courseC_maze_programming6_2024.json +49 -0
  187. maze/levels/courseC_maze_programming7_2023.json +54 -0
  188. maze/levels/courseC_maze_programming7_2024.json +54 -0
  189. maze/levels/courseC_maze_programming8_2023.json +52 -0
  190. maze/levels/courseC_maze_programming8_2024.json +51 -0
  191. maze/levels/courseC_maze_programming8_predict1_2024.json +45 -0
  192. maze/levels/courseC_maze_programming9_2023.json +47 -0
  193. maze/levels/courseC_maze_programming9_2024.json +47 -0
  194. maze/levels/courseC_maze_programming_challenge1_2023.json +47 -0
  195. maze/levels/courseC_maze_programming_challenge1_2024.json +47 -0
  196. maze/levels/courseC_maze_programming_challenge2_2023.json +49 -0
  197. maze/levels/courseC_maze_programming_challenge2_2024.json +49 -0
  198. maze/levels/courseD_bee_conditionals10_2023.json +57 -0
  199. maze/levels/courseD_bee_conditionals10_2024.json +57 -0
  200. maze/levels/courseD_bee_conditionals10_2025.json +57 -0
  201. maze/levels/courseD_bee_conditionals1_predict1_2023.json +51 -0
  202. maze/levels/courseD_bee_conditionals1_predict1_2024.json +51 -0
  203. maze/levels/courseD_bee_conditionals1_predict1_2025.json +51 -0
  204. maze/levels/courseD_bee_conditionals2_2023.json +60 -0
  205. maze/levels/courseD_bee_conditionals2_2024.json +60 -0
  206. maze/levels/courseD_bee_conditionals2_2025.json +60 -0
  207. maze/levels/courseD_bee_conditionals3_2023.json +57 -0
  208. maze/levels/courseD_bee_conditionals3_2024.json +57 -0
  209. maze/levels/courseD_bee_conditionals3_2025.json +57 -0
  210. maze/levels/courseD_bee_conditionals4_2023.json +58 -0
  211. maze/levels/courseD_bee_conditionals4_2024.json +58 -0
  212. maze/levels/courseD_bee_conditionals4_2025.json +58 -0
  213. maze/levels/courseD_bee_conditionals5_2023.json +58 -0
  214. maze/levels/courseD_bee_conditionals5_2024.json +58 -0
  215. maze/levels/courseD_bee_conditionals5_2025.json +58 -0
  216. maze/levels/courseD_bee_conditionals6_2023.json +57 -0
  217. maze/levels/courseD_bee_conditionals6_2024.json +57 -0
  218. maze/levels/courseD_bee_conditionals6_2025.json +57 -0
  219. maze/levels/courseD_bee_conditionals7_2023.json +57 -0
  220. maze/levels/courseD_bee_conditionals7_2024.json +57 -0
  221. maze/levels/courseD_bee_conditionals7_2025.json +57 -0
  222. maze/levels/courseD_bee_conditionals7_predict2_2023.json +54 -0
  223. maze/levels/courseD_bee_conditionals7_predict2_2024.json +54 -0
  224. maze/levels/courseD_bee_conditionals7_predict2_2025.json +54 -0
  225. maze/levels/courseD_bee_conditionals8_2023.json +57 -0
  226. maze/levels/courseD_bee_conditionals8_2024.json +57 -0
  227. maze/levels/courseD_bee_conditionals8_2025.json +57 -0
  228. maze/levels/courseD_bee_conditionals9_2023.json +55 -0
  229. maze/levels/courseD_bee_conditionals9_2024.json +55 -0
  230. maze/levels/courseD_bee_conditionals9_2025.json +55 -0
  231. maze/levels/courseD_bee_conditionals_challenge1_2023.json +57 -0
  232. maze/levels/courseD_bee_conditionals_challenge1_2024.json +57 -0
  233. maze/levels/courseD_bee_conditionals_challenge1_2025.json +57 -0
  234. maze/levels/courseD_bee_conditionals_challenge2_2023.json +58 -0
  235. maze/levels/courseD_bee_conditionals_challenge2_2024.json +58 -0
  236. maze/levels/courseD_bee_conditionals_challenge2_2025.json +58 -0
  237. maze/levels/courseD_bee_nestedLoops1.json +49 -0
  238. maze/levels/courseD_bee_nestedLoops1_2023.json +53 -0
  239. maze/levels/courseD_bee_nestedLoops1_2024.json +53 -0
  240. maze/levels/courseD_bee_nestedLoops1_2025.json +53 -0
  241. maze/levels/courseD_bee_nestedLoops1a.json +49 -0
  242. maze/levels/courseD_bee_nestedLoops1a_2023.json +55 -0
  243. maze/levels/courseD_bee_nestedLoops1a_2024.json +55 -0
  244. maze/levels/courseD_bee_nestedLoops1a_2025.json +55 -0
  245. maze/levels/courseD_bee_nestedLoops2.json +50 -0
  246. maze/levels/courseD_bee_nestedLoops2_2023.json +57 -0
  247. maze/levels/courseD_bee_nestedLoops2_2024.json +57 -0
  248. maze/levels/courseD_bee_nestedLoops2_2025.json +57 -0
  249. maze/levels/courseD_bee_nestedLoops2_predict1.json +51 -0
  250. maze/levels/courseD_bee_nestedLoops2_predict1_2023.json +51 -0
  251. maze/levels/courseD_bee_nestedLoops2_predict1_2024.json +51 -0
  252. maze/levels/courseD_bee_nestedLoops2_predict1_2025.json +51 -0
  253. maze/levels/courseD_bee_nestedLoops7.json +53 -0
  254. maze/levels/courseD_bee_nestedLoops7_2023.json +59 -0
  255. maze/levels/courseD_bee_nestedLoops7_2024.json +59 -0
  256. maze/levels/courseD_bee_nestedLoops7_2025.json +59 -0
  257. maze/levels/courseD_bee_nestedLoops8.json +52 -0
  258. maze/levels/courseD_bee_nestedLoops8_2023.json +59 -0
  259. maze/levels/courseD_bee_nestedLoops8_2024.json +59 -0
  260. maze/levels/courseD_bee_nestedLoops8_2025.json +59 -0
  261. maze/levels/courseD_bee_nestedLoops9.json +53 -0
  262. maze/levels/courseD_bee_nestedLoops9_2023.json +60 -0
  263. maze/levels/courseD_bee_nestedLoops9_2024.json +60 -0
  264. maze/levels/courseD_bee_nestedLoops9_2025.json +60 -0
  265. maze/levels/courseD_bee_nestedLoops9_predict2.json +51 -0
  266. maze/levels/courseD_bee_nestedLoops9_predict2_2023.json +54 -0
  267. maze/levels/courseD_bee_nestedLoops9_predict2_2024.json +54 -0
  268. maze/levels/courseD_bee_nestedLoops9_predict2_2025.json +54 -0
  269. maze/levels/courseD_collector_debugging1a_2023.json +50 -0
  270. maze/levels/courseD_collector_debugging1a_2024.json +50 -0
  271. maze/levels/courseD_collector_debugging1a_2025.json +50 -0
  272. maze/levels/courseD_collector_debugging2a_2023.json +50 -0
  273. maze/levels/courseD_collector_debugging2a_2024.json +50 -0
  274. maze/levels/courseD_collector_debugging2a_2025.json +50 -0
  275. maze/levels/courseD_collector_debugging3a_2023.json +50 -0
  276. maze/levels/courseD_collector_debugging3a_2024.json +50 -0
  277. maze/levels/courseD_collector_debugging3a_2025.json +50 -0
  278. maze/levels/courseD_collector_debugging4a_2023.json +50 -0
  279. maze/levels/courseD_collector_debugging4a_2024.json +50 -0
  280. maze/levels/courseD_collector_debugging4a_2025.json +50 -0
  281. maze/levels/courseD_collector_debugging5a_2023.json +50 -0
  282. maze/levels/courseD_collector_debugging5a_2024.json +50 -0
  283. maze/levels/courseD_collector_debugging5a_2025.json +50 -0
  284. maze/levels/courseD_collector_debugging6a_2023.json +56 -0
  285. maze/levels/courseD_collector_debugging6a_2024.json +56 -0
  286. maze/levels/courseD_collector_debugging6a_2025.json +56 -0
  287. maze/levels/courseD_collector_debugging8a_2023.json +49 -0
  288. maze/levels/courseD_collector_debugging8a_2024.json +49 -0
  289. maze/levels/courseD_collector_debugging8a_2025.json +49 -0
  290. maze/levels/courseD_collector_debugging9a_2023.json +49 -0
  291. maze/levels/courseD_collector_debugging9a_2024.json +49 -0
  292. maze/levels/courseD_collector_debugging9a_2025.json +49 -0
  293. maze/levels/courseD_collector_nested_loops_challenge2_2024.json +50 -0
  294. maze/levels/courseD_collector_nested_loops_challenge2_2025.json +50 -0
  295. maze/levels/courseD_farmer_while10_2023.json +50 -0
  296. maze/levels/courseD_farmer_while10_2024.json +50 -0
  297. maze/levels/courseD_farmer_while10_2025.json +50 -0
  298. maze/levels/courseD_farmer_while10_predict2_2024.json +45 -0
  299. maze/levels/courseD_farmer_while10_predict2_2025.json +45 -0
  300. maze/levels/courseD_farmer_while1_2023.json +49 -0
  301. maze/levels/courseD_farmer_while1_2024.json +49 -0
  302. maze/levels/courseD_farmer_while1_2025.json +49 -0
  303. maze/levels/courseD_farmer_while2_2023.json +45 -0
  304. maze/levels/courseD_farmer_while2_2024.json +45 -0
  305. maze/levels/courseD_farmer_while2_2025.json +45 -0
  306. maze/levels/courseD_farmer_while3_2023.json +47 -0
  307. maze/levels/courseD_farmer_while3_2024.json +47 -0
  308. maze/levels/courseD_farmer_while3_2025.json +47 -0
  309. maze/levels/courseD_farmer_while4_2023.json +50 -0
  310. maze/levels/courseD_farmer_while4_2024.json +50 -0
  311. maze/levels/courseD_farmer_while4_2025.json +50 -0
  312. maze/levels/courseD_farmer_while4_predict1_2024.json +47 -0
  313. maze/levels/courseD_farmer_while4_predict1_2025.json +47 -0
  314. maze/levels/courseD_farmer_while5_2023.json +52 -0
  315. maze/levels/courseD_farmer_while5_2024.json +52 -0
  316. maze/levels/courseD_farmer_while5_2025.json +52 -0
  317. maze/levels/courseD_farmer_while6_2023.json +51 -0
  318. maze/levels/courseD_farmer_while6_2024.json +51 -0
  319. maze/levels/courseD_farmer_while6_2025.json +51 -0
  320. maze/levels/courseD_farmer_while7_2023.json +52 -0
  321. maze/levels/courseD_farmer_while7_2024.json +52 -0
  322. maze/levels/courseD_farmer_while7_2025.json +52 -0
  323. maze/levels/courseD_farmer_while8_2023.json +51 -0
  324. maze/levels/courseD_farmer_while8_2024.json +51 -0
  325. maze/levels/courseD_farmer_while8_2025.json +51 -0
  326. maze/levels/courseD_farmer_while9_2023.json +50 -0
  327. maze/levels/courseD_farmer_while9_2024.json +50 -0
  328. maze/levels/courseD_farmer_while9_2025.json +50 -0
  329. maze/levels/courseD_farmer_while_challenge1_2023.json +50 -0
  330. maze/levels/courseD_farmer_while_challenge1_2024.json +50 -0
  331. maze/levels/courseD_farmer_while_challenge1_2025.json +50 -0
  332. maze/levels/courseD_harvester_nested_loops_challenge1_2024.json +51 -0
  333. maze/levels/courseD_harvester_nested_loops_challenge1_2025.json +51 -0
  334. maze/levels/courseD_iceage_loops10_2024.json +48 -0
  335. maze/levels/courseD_iceage_loops10_2025.json +48 -0
  336. maze/levels/courseD_iceage_loops11_2024.json +46 -0
  337. maze/levels/courseD_iceage_loops11_2025.json +46 -0
  338. maze/levels/courseD_iceage_loops12_2024.json +46 -0
  339. maze/levels/courseD_iceage_loops12_2025.json +46 -0
  340. maze/levels/courseD_iceage_loops1_2024.json +49 -0
  341. maze/levels/courseD_iceage_loops1_2025.json +49 -0
  342. maze/levels/courseD_iceage_loops2_2024.json +51 -0
  343. maze/levels/courseD_iceage_loops2_2025.json +51 -0
  344. maze/levels/courseD_iceage_loops3_2024.json +51 -0
  345. maze/levels/courseD_iceage_loops3_2025.json +51 -0
  346. maze/levels/courseD_iceage_loops4_2024.json +47 -0
  347. maze/levels/courseD_iceage_loops4_2025.json +47 -0
  348. maze/levels/courseD_iceage_loops6_2024.json +47 -0
  349. maze/levels/courseD_iceage_loops6_2025.json +47 -0
  350. maze/levels/courseD_iceage_loops7_2024.json +47 -0
  351. maze/levels/courseD_iceage_loops7_2025.json +47 -0
  352. maze/levels/courseD_iceage_loops8_2024.json +49 -0
  353. maze/levels/courseD_iceage_loops8_2025.json +49 -0
  354. maze/levels/courseD_iceage_loops9_2024.json +49 -0
  355. maze/levels/courseD_iceage_loops9_2025.json +49 -0
  356. maze/levels/courseD_maze_intro4_2023.json +45 -0
  357. maze/levels/courseD_maze_intro4_2024.json +45 -0
  358. maze/levels/courseD_maze_intro4_2025.json +45 -0
  359. maze/levels/courseD_maze_intro5a_2023.json +45 -0
  360. maze/levels/courseD_maze_intro5a_2024.json +45 -0
  361. maze/levels/courseD_maze_intro5a_2025.json +45 -0
  362. maze/levels/courseD_maze_intro5c_2023.json +45 -0
  363. maze/levels/courseD_maze_intro5c_2024.json +45 -0
  364. maze/levels/courseD_maze_intro5c_2025.json +45 -0
  365. maze/levels/courseD_maze_nestedLoops3.json +46 -0
  366. maze/levels/courseD_maze_nestedLoops3_2023.json +47 -0
  367. maze/levels/courseD_maze_nestedLoops3_2024.json +47 -0
  368. maze/levels/courseD_maze_nestedLoops3_2025.json +47 -0
  369. maze/levels/courseD_maze_nestedLoops4.json +45 -0
  370. maze/levels/courseD_maze_nestedLoops4_2023.json +47 -0
  371. maze/levels/courseD_maze_nestedLoops4_2024.json +47 -0
  372. maze/levels/courseD_maze_nestedLoops4_2025.json +47 -0
  373. maze/levels/courseD_maze_nestedLoops5.json +44 -0
  374. maze/levels/courseD_maze_nestedLoops5_2023.json +45 -0
  375. maze/levels/courseD_maze_nestedLoops5_2024.json +45 -0
  376. maze/levels/courseD_maze_nestedLoops5_2025.json +45 -0
  377. maze/levels/courseD_maze_nestedLoops6.json +43 -0
  378. maze/levels/courseD_maze_nestedLoops6_2023.json +45 -0
  379. maze/levels/courseD_maze_nestedLoops6_2024.json +45 -0
  380. maze/levels/courseD_maze_nestedLoops6_2025.json +45 -0
  381. maze/levels/courseD_maze_ramp1.json +47 -0
  382. maze/levels/courseD_maze_ramp1_2023.json +45 -0
  383. maze/levels/courseD_maze_ramp1_2024.json +45 -0
  384. maze/levels/courseD_maze_ramp1_2025.json +45 -0
  385. maze/levels/courseD_maze_ramp2.json +47 -0
  386. maze/levels/courseD_maze_ramp2_2023.json +45 -0
  387. maze/levels/courseD_maze_ramp2_2024.json +45 -0
  388. maze/levels/courseD_maze_ramp2_2025.json +45 -0
  389. maze/levels/courseD_maze_ramp3.json +46 -0
  390. maze/levels/courseD_maze_ramp3_2023.json +45 -0
  391. maze/levels/courseD_maze_ramp3_2024.json +45 -0
  392. maze/levels/courseD_maze_ramp3_2025.json +45 -0
  393. maze/levels/courseD_maze_ramp4.json +47 -0
  394. maze/levels/courseD_maze_ramp5.json +44 -0
  395. maze/levels/courseD_maze_ramp5_2023.json +42 -0
  396. maze/levels/courseD_maze_ramp5_2024.json +42 -0
  397. maze/levels/courseD_maze_ramp5_2025.json +42 -0
  398. maze/levels/courseD_maze_ramp5a.json +45 -0
  399. maze/levels/courseD_maze_ramp5b.json +45 -0
  400. maze/levels/courseD_maze_ramp5b_2023.json +43 -0
  401. maze/levels/courseD_maze_ramp5b_2024.json +43 -0
  402. maze/levels/courseD_maze_ramp5b_2025.json +43 -0
  403. maze/levels/courseD_maze_ramp5c.json +46 -0
  404. maze/levels/courseD_maze_ramp5d.json +45 -0
  405. maze/levels/courseD_maze_ramp5d_2023.json +43 -0
  406. maze/levels/courseD_maze_ramp5d_2024.json +43 -0
  407. maze/levels/courseD_maze_ramp5d_2025.json +43 -0
  408. maze/levels/courseD_maze_until10_2023.json +53 -0
  409. maze/levels/courseD_maze_until10_2024.json +53 -0
  410. maze/levels/courseD_maze_until10_2025.json +53 -0
  411. maze/levels/courseD_maze_until10_predict2_2024.json +52 -0
  412. maze/levels/courseD_maze_until10_predict2_2025.json +52 -0
  413. maze/levels/courseD_maze_until1_2023.json +47 -0
  414. maze/levels/courseD_maze_until1_2024.json +47 -0
  415. maze/levels/courseD_maze_until1_2025.json +47 -0
  416. maze/levels/courseD_maze_until2_predict1_2024.json +44 -0
  417. maze/levels/courseD_maze_until2_predict1_2025.json +44 -0
  418. maze/levels/courseD_maze_until3_2023.json +48 -0
  419. maze/levels/courseD_maze_until3_2024.json +48 -0
  420. maze/levels/courseD_maze_until3_2025.json +48 -0
  421. maze/levels/courseD_maze_until4_2023.json +49 -0
  422. maze/levels/courseD_maze_until4_2024.json +49 -0
  423. maze/levels/courseD_maze_until4_2025.json +49 -0
  424. maze/levels/courseD_maze_until5_2023.json +49 -0
  425. maze/levels/courseD_maze_until5_2024.json +49 -0
  426. maze/levels/courseD_maze_until5_2025.json +49 -0
  427. maze/levels/courseD_maze_until6_2023.json +49 -0
  428. maze/levels/courseD_maze_until6_2024.json +49 -0
  429. maze/levels/courseD_maze_until6_2025.json +49 -0
  430. maze/levels/courseD_maze_until7_2023.json +49 -0
  431. maze/levels/courseD_maze_until7_2024.json +49 -0
  432. maze/levels/courseD_maze_until7_2025.json +49 -0
  433. maze/levels/courseD_maze_until9_2023.json +53 -0
  434. maze/levels/courseD_maze_until9_2024.json +53 -0
  435. maze/levels/courseD_maze_until9_2025.json +53 -0
  436. maze/levels/courseD_scrat_predict1.json +44 -0
  437. maze/levels/courseD_scrat_ramp1.json +46 -0
  438. maze/levels/courseD_scrat_ramp2.json +47 -0
  439. maze/levels/courseD_scrat_ramp3.json +45 -0
  440. maze/levels/courseD_scrat_ramp4.json +46 -0
  441. maze/levels/courseD_scrat_ramp5.json +46 -0
  442. maze/levels/courseD_scrat_ramp6.json +47 -0
  443. maze/levels/courseD_scrat_ramp8.json +47 -0
  444. maze/levels/courseE_farmer_functions10a1_2023.json +55 -0
  445. maze/levels/courseE_farmer_functions10a1_2024.json +55 -0
  446. maze/levels/courseE_farmer_functions11_predict_2024.json +49 -0
  447. maze/levels/courseE_farmer_functions13_2023.json +55 -0
  448. maze/levels/courseE_farmer_functions13_2024.json +55 -0
  449. maze/levels/courseE_farmer_functions1_2023.json +54 -0
  450. maze/levels/courseE_farmer_functions1_2024.json +54 -0
  451. maze/levels/courseE_farmer_functions1a_2023.json +55 -0
  452. maze/levels/courseE_farmer_functions1a_2024.json +55 -0
  453. maze/levels/courseE_farmer_functions2b_2023.json +55 -0
  454. maze/levels/courseE_farmer_functions2b_2024.json +55 -0
  455. maze/levels/courseE_farmer_functions2ba_2023.json +55 -0
  456. maze/levels/courseE_farmer_functions2ba_2024.json +55 -0
  457. maze/levels/courseE_farmer_functions3c1_2023.json +56 -0
  458. maze/levels/courseE_farmer_functions3c1_2024.json +56 -0
  459. maze/levels/courseE_farmer_functions3c_2023.json +55 -0
  460. maze/levels/courseE_farmer_functions3c_2024.json +55 -0
  461. maze/levels/courseE_farmer_functions7a1_2023.json +59 -0
  462. maze/levels/courseE_farmer_functions7a1_2024.json +59 -0
  463. maze/levels/courseE_farmer_functions8a1_2023.json +56 -0
  464. maze/levels/courseE_farmer_functions8a1_2024.json +56 -0
  465. maze/levels/courseE_farmer_functions9a1_2023.json +58 -0
  466. maze/levels/courseE_farmer_functions9a1_2024.json +58 -0
  467. maze/levels/courseE_farmer_ramp11a_2023.json +49 -0
  468. maze/levels/courseE_farmer_ramp11a_2024.json +49 -0
  469. maze/levels/courseE_farmer_ramp11b_2023.json +50 -0
  470. maze/levels/courseE_farmer_ramp11b_2024.json +50 -0
  471. maze/levels/courseE_farmer_ramp12_forswap_2023.json +50 -0
  472. maze/levels/courseE_farmer_ramp12_forswap_2024.json +50 -0
  473. maze/levels/courseE_farmer_ramp12b_2023.json +50 -0
  474. maze/levels/courseE_farmer_ramp12b_2024.json +50 -0
  475. maze/levels/courseE_farmer_ramp12c_2023.json +50 -0
  476. maze/levels/courseE_farmer_ramp12c_2024.json +50 -0
  477. maze/levels/courseE_farmer_ramp12d_2023.json +50 -0
  478. maze/levels/courseE_farmer_ramp12d_2024.json +50 -0
  479. maze/levels/courseE_farmer_ramp12e_2023.json +50 -0
  480. maze/levels/courseE_farmer_ramp12e_2024.json +50 -0
  481. maze/levels/courseE_farmer_ramp12f_2023.json +51 -0
  482. maze/levels/courseE_farmer_ramp12f_2024.json +51 -0
  483. maze/levels/courseE_farmer_while1_2023.json +54 -0
  484. maze/levels/courseE_farmer_while1_2024.json +54 -0
  485. maze/levels/courseF_bee_conditionals10.json +53 -0
  486. maze/levels/courseF_bee_conditionals1_predict1.json +52 -0
  487. maze/levels/courseF_bee_conditionals2.json +53 -0
  488. maze/levels/courseF_bee_conditionals3.json +51 -0
  489. maze/levels/courseF_bee_conditionals4.json +50 -0
  490. maze/levels/courseF_bee_conditionals5.json +53 -0
  491. maze/levels/courseF_bee_conditionals7_predict2.json +53 -0
  492. maze/levels/courseF_bee_conditionals8.json +54 -0
  493. maze/levels/courseF_bee_for1.json +50 -0
  494. maze/levels/courseF_bee_for10.json +53 -0
  495. maze/levels/courseF_bee_for11.json +48 -0
  496. maze/levels/courseF_bee_for2.json +51 -0
  497. maze/levels/courseF_bee_for3_predict1.json +52 -0
  498. maze/levels/courseF_bee_for4.json +54 -0
  499. maze/levels/courseF_bee_for5.json +52 -0
  500. maze/levels/courseF_bee_for6.json +55 -0
  501. maze/levels/courseF_bee_for7.json +53 -0
  502. maze/levels/courseF_bee_for8.json +54 -0
  503. maze/levels/courseF_bee_for9.json +55 -0
  504. maze/levels/courseF_bee_for_challenge1.json +52 -0
  505. maze/levels/courseF_bee_for_challenge2.json +48 -0
  506. maze/levels/courseF_bee_functions2.json +55 -0
  507. maze/levels/courseF_bee_functions3.json +55 -0
  508. maze/levels/courseF_bee_functions3a.json +55 -0
  509. maze/levels/courseF_bee_functions4.json +54 -0
  510. maze/levels/courseF_bee_functions5a.json +55 -0
  511. maze/levels/courseF_bee_functions6.json +57 -0
  512. maze/levels/courseF_bee_functions7.json +55 -0
  513. maze/levels/courseF_bee_functions8a.json +52 -0
  514. maze/levels/courseF_bee_functionsPre7.json +56 -0
  515. maze/levels/courseF_bee_functions_challenge1.json +52 -0
  516. maze/levels/courseF_bee_functions_challenge2.json +51 -0
  517. maze/levels/courseF_bee_fwp1.json +52 -0
  518. maze/levels/courseF_bee_fwp2.json +52 -0
  519. maze/levels/courseF_bee_fwp3.json +54 -0
  520. maze/levels/courseF_bee_fwp4.json +52 -0
  521. maze/levels/courseF_bee_fwp5.json +52 -0
  522. maze/levels/courseF_bee_fwp6.json +53 -0
  523. maze/levels/courseF_bee_fwp7.json +52 -0
  524. maze/levels/courseF_bee_fwp8.json +55 -0
  525. maze/levels/courseF_bee_fwp9_predict1.json +54 -0
  526. maze/levels/courseF_bee_fwp_challenge1.json +47 -0
  527. maze/levels/courseF_bee_fwp_challenge2.json +48 -0
  528. maze/levels/coursea_maze_ramp1_2024.json +53 -0
  529. maze/levels/grade2_collector_10_2023.json +45 -0
  530. maze/levels/grade2_collector_10_2024.json +45 -0
  531. maze/levels/grade2_collector_A_2023.json +50 -0
  532. maze/levels/grade2_collector_A_2024.json +50 -0
  533. maze/levels/grade2_collector_A_predict1_2023.json +46 -0
  534. maze/levels/grade2_collector_A_predict1_2024.json +46 -0
  535. maze/maze.py +650 -0
  536. maze/puzzle.py +75 -0
  537. maze/pvz.py +62 -0
  538. maze/screencapture.py +22 -0
  539. maze/zombieshape.py +108 -0
maze/maze.py ADDED
@@ -0,0 +1,650 @@
1
+ from enum import Enum
2
+ import turtle
3
+ import math
4
+ import random
5
+ import functools
6
+ import json
7
+ import os
8
+ import sys
9
+ import importlib.resources
10
+
11
+ _TESTMODE = os.environ.get('TESTMODE')
12
+
13
+ _TRACER_DELAY = 0 if _TESTMODE == "True" else 15
14
+ _TRACER_N = 0 if _TESTMODE == "True" else 1
15
+
16
+ # Get the directory of this Python file
17
+ _DIR_PATH = os.path.dirname(os.path.realpath(__file__))
18
+
19
+
20
+ class Pen(turtle.Turtle):
21
+ def __init__(self):
22
+ super().__init__()
23
+ self.hideturtle()
24
+ self.speed(0)
25
+ self.penup()
26
+ self.color("white")
27
+
28
+ def draw_success(self):
29
+ self.goto(0, 100)
30
+ self.write("You did it!", False, "center", font=("Arial", 32, "bold"))
31
+
32
+ def draw_failure(self):
33
+ self.goto(0, 100)
34
+ self.color("red")
35
+ self.write("Oops, something is wrong", False, "center", font=("Arial", 28, "bold"))
36
+ self.goto(0, 50)
37
+ self.write("Try again!", False, "center", font=("Arial", 28, "bold"))
38
+
39
+ def tick(self):
40
+ self.getscreen().tracer(_TRACER_N, 50)
41
+ self.left(1)
42
+ self.right(2)
43
+ self.left(1)
44
+ self.getscreen().tracer(_TRACER_N, _TRACER_DELAY)
45
+
46
+
47
+ class Wall(turtle.Turtle):
48
+ def __init__(self):
49
+ turtle.Turtle.__init__(self)
50
+ self.shape("square")
51
+ self.color("green")
52
+ self.penup()
53
+ self.speed(0)
54
+
55
+
56
+ class Path(turtle.Turtle):
57
+ def __init__(self):
58
+ self._stamp = True
59
+ turtle.Turtle.__init__(self)
60
+ self.setheading(90)
61
+ self.shape(Maze.shapefile("path"))
62
+ self.color("brown")
63
+ self.penup()
64
+ self.speed(0)
65
+
66
+ def draw(self, x, y):
67
+ self.x = x
68
+ self.y = y
69
+ self.goto(x, y)
70
+ if self._stamp:
71
+ self.stamp()
72
+ else:
73
+ self.hideturtle()
74
+
75
+
76
+ class Cell(turtle.Turtle):
77
+
78
+ def __init__(self, tileType=0, value=0, range=0):
79
+ super().__init__()
80
+ self.tileType = SquareType(tileType)
81
+ self.value = value
82
+ self.originalValue = value
83
+ self.range = range
84
+
85
+ self.x = 0
86
+ self.y = 0
87
+
88
+ self.setheading(90)
89
+ self.color("white")
90
+ self.penup()
91
+ self.speed(0)
92
+
93
+ self.hideturtle()
94
+
95
+ def is_variable_range(self):
96
+ return self.value == self.originalValue and self.range != self.originalValue
97
+
98
+ def draw(self, x, y):
99
+ self.x = x
100
+ self.y = y
101
+
102
+ self.goto(x, y)
103
+ self.redraw()
104
+
105
+ def draw_value(self):
106
+ value = abs(self.value)
107
+ if self.is_variable_range():
108
+ value = '?'
109
+
110
+ delay = self.getscreen().delay()
111
+ if delay:
112
+ self.getscreen().tracer(0, 0)
113
+ self.clear()
114
+ self.setpos(self.x + 20, self.y - 20)
115
+ self.write(value, False, align="right", font=("Arial", 18, "bold"))
116
+ self.setpos(self.x, self.y)
117
+ if delay:
118
+ self.getscreen().tracer(_TRACER_N, delay)
119
+
120
+ def needs_visit(self):
121
+ return self.tileType.is_open()
122
+
123
+ def is_wall(self):
124
+ return self.tileType.is_wall()
125
+
126
+ def is_open(self):
127
+ return self.tileType.is_open()
128
+
129
+ def is_obstacle(self):
130
+ return self.tileType.is_obstacle()
131
+
132
+ def is_finish(self):
133
+ return self.tileType.is_finish()
134
+
135
+ @staticmethod
136
+ def sort_list(the_list):
137
+ the_list.sort(key=lambda cell: (cell.x, cell.y))
138
+
139
+ def redraw(self):
140
+ """
141
+ Method that will be called when the player moved, so the cell has a chance to redraw itself if needed
142
+ :return:
143
+ """
144
+
145
+
146
+ class SquareType(Enum):
147
+ WALL = 0
148
+ OPEN = 1
149
+ START = 2
150
+ FINISH = 3
151
+ OBSTACLE = 4
152
+ STARTANDFINISH = 5
153
+
154
+ def is_open(self):
155
+ return self.value > 0
156
+
157
+ def is_obstacle(self):
158
+ return self == SquareType.OBSTACLE
159
+
160
+ def is_wall(self):
161
+ return self.value == 0
162
+
163
+ def is_start(self):
164
+ return self in [SquareType.START, SquareType.STARTANDFINISH]
165
+
166
+ def is_finish(self):
167
+ return self in [SquareType.FINISH, SquareType.STARTANDFINISH]
168
+
169
+
170
+ class Direction(Enum):
171
+ NORTH = 0
172
+ EAST = 1
173
+ SOUTH = 2
174
+ WEST = 3
175
+
176
+ @classmethod
177
+ def _missing_(cls, value):
178
+ value = int(value)
179
+ for member in cls:
180
+ if member.value == value:
181
+ return member
182
+ return None
183
+
184
+ def left(self):
185
+ value = self.value - 1
186
+ if value < 0:
187
+ value = 3
188
+ return Direction(value)
189
+
190
+ def right(self):
191
+ value = self.value + 1
192
+ if value > 3:
193
+ value = 0
194
+ return Direction(value)
195
+
196
+ def to_heading(self):
197
+ """
198
+ Converts the direction of a Maze level to python turtle "standard" mode
199
+ 0 - east 1
200
+ 90 - north 0
201
+ 180 - west 3
202
+ 270 - south 2
203
+ """
204
+ if self == Direction.NORTH:
205
+ return 90
206
+ elif self == Direction.SOUTH:
207
+ return 270
208
+ elif self == Direction.WEST:
209
+ return 180
210
+
211
+ return 0
212
+
213
+ @staticmethod
214
+ def from_heading(head):
215
+ """
216
+ Returns a Direction based on the heading in degrees
217
+ """
218
+ if head == 90:
219
+ return Direction.NORTH
220
+ elif head == 270:
221
+ return Direction.SOUTH
222
+ elif head == 180:
223
+ return Direction.WEST
224
+
225
+ return Direction.EAST
226
+
227
+
228
+ class MazeType:
229
+
230
+ def __init__(self):
231
+ super().__init__()
232
+ self.subfolder = None
233
+
234
+ self.pathClass = Path
235
+ self.cellClass = Cell
236
+ self.playerClass = Player
237
+
238
+ self._path = None
239
+ self._wall = None
240
+
241
+ def new_player(self, maze):
242
+ return self.playerClass(maze)
243
+
244
+ def new_cell(self, cell_dict) -> Cell:
245
+ return self.cellClass(**cell_dict)
246
+
247
+ def path(self):
248
+ if self._path is None and self.pathClass:
249
+ self._path = self.pathClass()
250
+ return self._path
251
+
252
+ def wall(self):
253
+ """
254
+ Default implementation is to return the `self._wall` instance, even if it is `None`
255
+ A subclass can choose to instantiate a wall if they need it.
256
+ :return:
257
+ """
258
+ return self._wall
259
+
260
+ def setup(self, level, maze):
261
+ """
262
+ Method is called before starting to render and setup the Maze
263
+ """
264
+
265
+ def after_move(self, maze):
266
+ """
267
+ Method is called after each move, to handle any actions that are needed
268
+ """
269
+ if hasattr(self.cellClass, "reveal"):
270
+ coords = maze.player.gridcoords()
271
+ maze.get_cell((coords[0], coords[1])).reveal()
272
+ if coords[1] > 0: # reveal above
273
+ maze.get_cell((coords[0], coords[1] - 1)).reveal()
274
+ if coords[0] > 0: # reveal left
275
+ maze.get_cell((coords[0] - 1, coords[1])).reveal()
276
+ if coords[1] < len(maze.grid) - 1: # reveal below
277
+ maze.get_cell((coords[0], coords[1] + 1)).reveal()
278
+ if coords[0] < len(maze.grid[coords[1]]) - 1: # reveal right
279
+ maze.get_cell((coords[0] + 1, coords[1])).reveal()
280
+
281
+ def parse_cell_from_old_values(self, map_cell, initial_dirt_cell):
282
+ """
283
+ Simplest method to parse the cell
284
+ """
285
+ map_cell = str(map_cell)
286
+ if initial_dirt_cell:
287
+ initial_dirt_cell = int(initial_dirt_cell)
288
+
289
+ tile_type = int(map_cell)
290
+ value = initial_dirt_cell
291
+
292
+ return dict(tileType=tile_type, value=value)
293
+
294
+ def success(self, maze, player):
295
+ player._success()
296
+
297
+ def failure(self, maze, player, reason=""):
298
+ player._fail(False, reason)
299
+
300
+ def detect_win_scenario(self, maze):
301
+ if all(element in maze.visited for element in maze.cells_to_visit):
302
+ value = maze.get_cell_values()
303
+ return value == 0
304
+ return False
305
+
306
+
307
+ class Maze:
308
+
309
+ @staticmethod
310
+ def _testmode():
311
+ global _TRACER_DELAY
312
+ global _TRACER_N
313
+ _TRACER_DELAY = 0
314
+ _TRACER_N = 0
315
+
316
+ # @staticmethod
317
+ # def shapefile(name: str, ext: str = ".gif") -> str:
318
+ # name = os.path.join(Maze.instance.maze_type.subfolder, name)
319
+ # return os.path.join(_DIR_PATH, "images", name + ext)
320
+
321
+ @staticmethod
322
+ def shapefile(name: str, ext: str = ".gif") -> str:
323
+ with importlib.resources.path('maze.images.' + Maze.instance.maze_type.subfolder, name + ext) as image_path:
324
+ return image_path.__str__()
325
+
326
+ instance = None
327
+
328
+ def __init__(self, level: dict, maze_type: MazeType):
329
+ Maze.instance: Maze = self
330
+ self.maze_type: MazeType = maze_type
331
+
332
+ self.screen = turtle.Screen()
333
+ self.walls = []
334
+ self.player = None
335
+ self.path = None
336
+
337
+ self.width = 0
338
+ self.height = 0
339
+
340
+ self.grid = []
341
+
342
+ self.cells_to_visit = []
343
+ self.visited = []
344
+
345
+ self.pen = Pen()
346
+
347
+ maze_type.setup(level, self)
348
+
349
+ self.screen.bgcolor("white")
350
+ self.screen.setup(410, 410) # TODO Add dynamic size
351
+ self.screen.tracer(0, 0)
352
+
353
+ self._setup_maze(level)
354
+
355
+ self.pen.tick()
356
+
357
+ def parse_maze(self, level_props):
358
+ maze = json.loads(level_props["maze"])
359
+ initial_dirt_matrix = level_props.get("initial_dirt")
360
+ initial_dirt = None
361
+ if initial_dirt_matrix:
362
+ initial_dirt = json.loads(initial_dirt_matrix)
363
+
364
+ parsed_maze = []
365
+ for r in range(len(maze)):
366
+ row = []
367
+ parsed_maze.append(row)
368
+ for c in range(len(maze[r])):
369
+ cell = self.maze_type.parse_cell_from_old_values(maze[r][c],
370
+ initial_dirt[r][c] if initial_dirt else None)
371
+ row.append(cell)
372
+
373
+ return parsed_maze
374
+
375
+ def _setup_maze(self, level):
376
+ level_props = level['properties']
377
+
378
+ step_speed = level_props.get("step_speed")
379
+
380
+ if step_speed and not _TESTMODE:
381
+ global _TRACER_DELAY
382
+ _TRACER_DELAY = 15 / int(step_speed)
383
+
384
+ maze = None
385
+ if level_props.get("serialized_maze"):
386
+ maze = json.loads(level_props.get("serialized_maze"))
387
+
388
+ if not maze:
389
+ maze = self.parse_maze(level_props)
390
+
391
+ rows = len(maze)
392
+ self.height = rows * 50
393
+ self.grid = []
394
+
395
+ for y in range(rows):
396
+ row = []
397
+ self.grid.append(row)
398
+ cols = len(maze[y])
399
+ for x in range(cols):
400
+ if self.width == 0:
401
+ self.width = cols * 50
402
+
403
+ cell = self.maze_type.new_cell(maze[y][x])
404
+ row.append(cell)
405
+
406
+ screen_x = -1 * ((self.width / 2) - 25) + (x * 50)
407
+ screen_y = (self.height / 2 - 25) - (y * 50)
408
+
409
+ if cell.tileType.is_open():
410
+ if self.maze_type.path():
411
+ self.maze_type.path().draw(screen_x, screen_y)
412
+ if cell.needs_visit():
413
+ self.cells_to_visit.append(cell)
414
+
415
+ cell.draw(screen_x, screen_y)
416
+
417
+ if cell.tileType == SquareType.WALL:
418
+ self.walls.append((x, y))
419
+
420
+ if cell.tileType.is_start():
421
+ self.startCell = cell
422
+
423
+ if self.maze_type.path():
424
+ self.maze_type.path().hideturtle()
425
+ Cell.sort_list(self.cells_to_visit)
426
+
427
+ self.player = self.maze_type.new_player(self)
428
+ self.player._turtle.goto(self.startCell.x, self.startCell.y)
429
+
430
+ direction = level_props.get("start_direction")
431
+ if direction:
432
+ direction = Direction(direction)
433
+ self.player._turtle.setheading(direction.to_heading())
434
+
435
+ self.update()
436
+
437
+ def update(self):
438
+ coords = self.player.gridcoords()
439
+ cell = self.get_cell(coords)
440
+ if not cell in self.visited:
441
+ self.visited.append(cell)
442
+ Cell.sort_list(self.visited)
443
+
444
+ self.maze_type.after_move(self)
445
+
446
+ self.pen.tick()
447
+
448
+ def done(self):
449
+ if self.detect_win_scenario():
450
+ self._success()
451
+ else:
452
+ self._fail("Win scenario not met")
453
+
454
+ def _success(self):
455
+ self.maze_type.success(self, self.player)
456
+
457
+ def _fail(self, reason=""):
458
+ self.maze_type.failure(self, self.player, reason)
459
+
460
+ def detect_win_scenario(self):
461
+ return self.maze_type.detect_win_scenario(self)
462
+
463
+ def get_cell_values(self):
464
+ """
465
+ Returns the sum of the value of all the cells that were visited
466
+ :return:
467
+ """
468
+ return functools.reduce(lambda v, cell: v + (cell.value if cell.value else 0), self.visited, 0)
469
+
470
+ def get_cell(self, gridcoords: (int, int)) -> Cell:
471
+ row = None
472
+ if len(self.grid) > gridcoords[1] >= 0:
473
+ row = self.grid[gridcoords[1]]
474
+ if row is not None and len(row) > gridcoords[0] >= 0:
475
+ return row[gridcoords[0]]
476
+ return None
477
+
478
+ def is_wall(self, gridcoords):
479
+ cell = self.get_cell(gridcoords)
480
+ return cell.tileType.is_wall()
481
+
482
+ def is_path_in_heading(self, start_yx, heading):
483
+ cell: Cell = None
484
+ if heading == Direction.NORTH.to_heading():
485
+ cell = self.get_cell((start_yx[0], start_yx[1] - 1))
486
+ elif heading == Direction.EAST.to_heading():
487
+ cell = self.get_cell((start_yx[0] + 1, start_yx[1]))
488
+ elif heading == Direction.SOUTH.to_heading():
489
+ cell = self.get_cell((start_yx[0], start_yx[1] + 1))
490
+ elif heading == Direction.WEST.to_heading():
491
+ cell = self.get_cell((start_yx[0] - 1, start_yx[1]))
492
+
493
+ return cell is not None and cell.is_open()
494
+
495
+
496
+ class Player:
497
+ maze: Maze = None
498
+ instance = None
499
+
500
+ _turtle = turtle.Turtle()
501
+
502
+ def __init__(self, maze):
503
+ Player.instance = self
504
+ self.maze = maze
505
+
506
+ self._turtle.penup()
507
+ self._turtle.speed(1)
508
+
509
+ def is_collision(self, other):
510
+ a = self._turtle.xcor() - other.turtle.xcor()
511
+ b = self._turtle.ycor() - other.turtle.ycor()
512
+ distance = math.sqrt((a ** 2) + (b ** 2))
513
+
514
+ if distance < 5:
515
+ return True
516
+ else:
517
+ return False
518
+
519
+ def gridcoords(self):
520
+ """Return the turtle's current location witin the grid system (col,row), as a Vec2D-vector.
521
+
522
+ No arguments.
523
+
524
+ Example (for a Turtle instance named pegman):
525
+ >>> pegman.pos()
526
+ (1, 8)
527
+ """
528
+ col = round((self.maze.width / 2 + (self._turtle.xcor() - 25)) / 50)
529
+ row = round((self.maze.height / 2 - (self._turtle.ycor() + 25)) / 50)
530
+ return col, row
531
+
532
+ def _get_current_cell(self) -> Cell:
533
+ return self.maze.get_cell(self.gridcoords())
534
+
535
+ def _check(self):
536
+ if self.maze.is_wall(self.gridcoords()):
537
+ self._fail(True, "stepped into wall")
538
+ if self._get_current_cell().is_obstacle():
539
+ self._fail(False, "stepped into obstacle")
540
+
541
+ self.maze.update()
542
+
543
+ def _fail(self, undo=True, reason=""):
544
+ print("fail", reason)
545
+ if _TRACER_N > 0:
546
+ if undo:
547
+ self._turtle.undo()
548
+ self._turtle.color("black", "red")
549
+ self.maze.pen.draw_failure()
550
+ self._turtle.getscreen().mainloop()
551
+ if _TESTMODE == "True":
552
+ sys.exit()
553
+
554
+ def _success(self):
555
+ print("success")
556
+ if _TRACER_N > 0:
557
+ self.maze.pen.draw_success()
558
+ self._turtle.speed(8)
559
+ self._turtle.right(360)
560
+ self._turtle.left(360)
561
+ self._turtle.getscreen().mainloop()
562
+ if _TESTMODE == "True":
563
+ sys.exit()
564
+
565
+ def _process(self, cond, deduct=1):
566
+ color = self._turtle.color()
567
+ self._turtle.color("black", "orange")
568
+ cell = self._get_current_cell()
569
+ if cond(cell):
570
+ if (deduct > 0 >= cell.value) or (deduct < 0 <= cell.value):
571
+ self._fail(False, "processed cell with wrong value")
572
+ cell.value -= deduct
573
+ cell.redraw()
574
+ else:
575
+ self._fail(False, "wrong action used to process cell")
576
+
577
+ self._turtle.color(color[0], color[1])
578
+
579
+ def _get_value_if(self, cell_condition):
580
+ cell = self._get_current_cell()
581
+ value = 0
582
+ if cell_condition(cell):
583
+ value = cell.value
584
+ return value
585
+
586
+ def forward(self, steps=1):
587
+ for i in range(steps):
588
+ valid_move = self.path_ahead()
589
+ self._turtle.forward(50)
590
+ if not valid_move:
591
+ self._fail(undo=True, reason="no path ahead")
592
+ else:
593
+ self._check()
594
+
595
+ def backward(self, steps=1):
596
+ for i in range(steps):
597
+ self._turtle.backward(50)
598
+ self._check()
599
+
600
+ def right(self):
601
+ self._turtle.right(90)
602
+
603
+ def left(self):
604
+ self._turtle.left(90)
605
+
606
+ def east(self):
607
+ self._move_compass_direction(Direction.EAST)
608
+
609
+ def west(self):
610
+ self._move_compass_direction(Direction.WEST)
611
+
612
+ def north(self):
613
+ self._move_compass_direction(Direction.NORTH)
614
+
615
+ def south(self):
616
+ self._move_compass_direction(Direction.SOUTH)
617
+
618
+ def _move_compass_direction(self, direction:Direction):
619
+ self._turtle.setheading(direction.to_heading())
620
+ self.forward()
621
+
622
+ # Move Aliases
623
+ move_forward = forward
624
+ go_forward = forward
625
+ move_east = east
626
+ move_west = west
627
+ move_north = north
628
+ move_south = south
629
+
630
+ def path_ahead(self):
631
+ coords = self.gridcoords()
632
+ head = self._turtle.heading()
633
+ return self.maze.is_path_in_heading(coords, head)
634
+
635
+ def path_left(self):
636
+ coords = self.gridcoords()
637
+ d = Direction.from_heading(self._turtle.heading()).left()
638
+ return self.maze.is_path_in_heading(coords, d.to_heading())
639
+
640
+ def path_right(self):
641
+ coords = self.gridcoords()
642
+ d = Direction.from_heading(self._turtle.heading()).right()
643
+ return self.maze.is_path_in_heading(coords, d.to_heading())
644
+
645
+ def at_finish(self):
646
+ """
647
+ Is the player at the finish point yet?
648
+ :return:
649
+ """
650
+ return self._get_current_cell().is_finish()
maze/puzzle.py ADDED
@@ -0,0 +1,75 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ import importlib.resources
5
+
6
+ from .maze import Maze, Player
7
+ from .bee import BeeMazeType
8
+ from .pvz import ZombieMazeType
9
+ from .farmer import FarmerMazeType
10
+ from .harvester import HarvesterMazeType
11
+ from .birds import BirdsMazeType
12
+ from .collector import CollectorMazeType
13
+
14
+ # Get the directory of the Python file being run
15
+ _script_path = os.path.dirname(os.path.realpath(sys.argv[0]))
16
+
17
+
18
+ def _find_level_file(filename):
19
+ """
20
+ Locates a level json file, preferring a local/dev copy (e.g. one just generated by the
21
+ importer and not yet copied into maze/levels) over the one packaged with the maze module.
22
+ Returns an open()-able path/traversable, or None if not found.
23
+ """
24
+ if not filename.endswith(".json"):
25
+ filename = f"{filename}.json"
26
+
27
+ for candidate in (
28
+ filename,
29
+ os.path.join(_script_path, filename),
30
+ os.path.join(_script_path, "levels", filename),
31
+ ):
32
+ if os.path.exists(candidate):
33
+ return candidate
34
+
35
+ packaged = importlib.resources.files("maze.levels").joinpath(os.path.basename(filename))
36
+ if packaged.is_file():
37
+ return packaged
38
+
39
+ return None
40
+
41
+
42
+ class Puzzle:
43
+ mazeTypes = {
44
+ "bee": BeeMazeType(),
45
+ "pvz": ZombieMazeType(),
46
+ "farmer": FarmerMazeType(),
47
+ "harvester": HarvesterMazeType(),
48
+ "birds": BirdsMazeType(),
49
+ "collector": CollectorMazeType()
50
+ }
51
+
52
+ @staticmethod
53
+ def from_file(filename):
54
+ resolved = _find_level_file(filename)
55
+ if resolved is None:
56
+ raise TypeError("Unable to find file for level " + filename)
57
+
58
+ # `resolved` is either a plain path (str) or an importlib.resources Traversable
59
+ opener = resolved.open if hasattr(resolved, "open") else lambda: open(resolved)
60
+ with opener() as fp:
61
+ level_json = json.load(fp)
62
+
63
+ game_id = level_json.get("game_id")
64
+ if game_id and int(game_id) == 25:
65
+ levelProps = level_json['properties']
66
+
67
+ mazeType = Puzzle.mazeTypes[levelProps["skin"]]
68
+
69
+ return Maze(level_json, mazeType)
70
+ else:
71
+ print(f"Unknown game_id: {game_id}")
72
+
73
+ @staticmethod
74
+ def done():
75
+ Maze.instance.done()