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
@@ -0,0 +1,150 @@
1
+ import xml.etree.ElementTree as ET
2
+ import json
3
+ import argparse
4
+ import os
5
+ import shutil
6
+
7
+ import jinja2
8
+
9
+ from .code_blocks import generate_toolbox_python, generate_python_from_blocks
10
+
11
+
12
+ def create_levels_json(levelfiles: list, target_dir):
13
+ if isinstance(levelfiles, str):
14
+ levelfiles = [levelfiles]
15
+
16
+ loaded_json = []
17
+
18
+ for levelfile in levelfiles:
19
+ try:
20
+ print("creating level for", levelfile)
21
+ root = ET.parse(levelfile)
22
+ loaded_json.append(extract_level_json(levelfile, root, target_dir))
23
+ except (AttributeError, KeyError) as err:
24
+ print(f"Error handling file: {levelfile} : ", err)
25
+
26
+ return loaded_json
27
+
28
+
29
+ def extract_level_json(levelfile, root, target_dir):
30
+ # Find the 'config' node and extract the JSON data
31
+ config_node = root.find('config')
32
+ json_data = config_node.text
33
+
34
+ # Load the JSON data as a Python dictionary
35
+ data = json.loads(json_data)
36
+
37
+ filename = os.path.basename(levelfile)
38
+ filename = os.path.splitext(filename)[0]
39
+ filename = f"{target_dir}/{filename}.json"
40
+
41
+ # Save the Python dictionary as a JSON file
42
+ with open(filename, 'w') as f:
43
+ json.dump(data, f, indent=2)
44
+ print(f" -- Saved json to '{filename}'")
45
+
46
+ return data
47
+
48
+
49
+ # Get the directory of the current Python file
50
+ _DIR_PATH = os.path.dirname(os.path.realpath(__file__))
51
+
52
+ SKIN_PLAYER = {
53
+ # '[SKIN NAME]', ('[VARIABLE NAME IN PY]', '[CLASS NAME IN PY]')
54
+ 'pvz': ('zombie', 'ZombiePlayer'),
55
+ 'harvester': ('farmer', 'Harvester'),
56
+ 'farmer': ('farmer', 'Farmer'),
57
+ 'bee': ('bee', 'BeePlayer'),
58
+ 'birds': ('bird', 'Bird'),
59
+ 'collector': ('collector', 'Collector')
60
+ }
61
+
62
+
63
+ def create_maze_python(level_filename: str, maze_lesson_dirname: str, level_key, course, lesson, level, level_xml):
64
+ environment = jinja2.Environment(loader=jinja2.FileSystemLoader(_DIR_PATH))
65
+ skin = level['config']['properties'].get('skin')
66
+
67
+ contained_levels = level['config']['properties'].get("contained_level_names")
68
+
69
+ mapping = SKIN_PLAYER.get(skin)
70
+ if mapping:
71
+ (player, player_type) = mapping
72
+ else:
73
+ print(f" -- No player mapping found for {skin}")
74
+ return
75
+
76
+ template = environment.get_template(f"maze.py.j2")
77
+ toolbox = generate_toolbox_python(level_xml, player)
78
+ start_code = generate_python_from_blocks(level_xml.find("./blocks/start_blocks/xml"), player)
79
+ solution_code = generate_python_from_blocks(level_xml.find("./blocks/solution_blocks/xml"), player)
80
+
81
+ # Create the level / puzzle file
82
+ content = template.render(course=course, lesson=lesson, level=level, levelname=level_key, toolbox=toolbox,
83
+ player=player, player_type=player_type, start_code=start_code)
84
+
85
+ with open(f"{level_filename}.py", mode="w", encoding="utf-8") as file:
86
+ file.write(content)
87
+
88
+ # Create the solution / test file, in the mirrored maze-repo lesson directory (no student repo puzzle file alongside it)
89
+ content = template.render(course=course, lesson=lesson, level=level, levelname=level_key, toolbox=toolbox,
90
+ player=player, player_type=player_type, start_code=solution_code)
91
+
92
+ test_filename = os.path.join(maze_lesson_dirname, "test_" + os.path.basename(level_filename))
93
+
94
+ with open(f"{test_filename}.py", mode="w", encoding="utf-8") as file:
95
+ file.write(content)
96
+
97
+ # create a test_all.py file, if one does not exist
98
+ test_all_file = os.path.join(maze_lesson_dirname, "test_all.py")
99
+ print("test all file", test_all_file, "exist?", os.path.exists(test_all_file))
100
+ if not os.path.exists(test_all_file):
101
+ print("Copy test_all.py to ", test_all_file)
102
+ shutil.copy(os.path.join(_DIR_PATH, "test_all.py"), test_all_file)
103
+
104
+ print(f" -- wrote level to {level_filename}.py")
105
+
106
+
107
+ def handle_level(levelfile, leveltype, levels_dirname, maze_lesson_dirname, level_filename, level_key, course, lesson, level):
108
+ """
109
+
110
+ :param levelfile: the source level file, typically a `.level`
111
+ :param leveltype: e.g. maze
112
+ :param levels_dirname: the shared flat directory (maze repo's src/maze/levels) to store the packaged level json in
113
+ :param maze_lesson_dirname: the mirrored lesson directory in the maze repo, to store the test_ solution file in
114
+ :param level_filename: the target filename without extension, including directory
115
+ :param level_key: the name of the level, which is also the key, e.g. "While Loops in Farmer"
116
+ :param course: the course json
117
+ :param lesson: the lesson json
118
+ :param level: the level json
119
+ :return:
120
+ """
121
+ print("HANDLE LEVEL: ", os.path.basename(levelfile), "type:", leveltype, "target:", level_filename)
122
+ os.makedirs(levels_dirname, exist_ok=True)
123
+ os.makedirs(maze_lesson_dirname, exist_ok=True)
124
+
125
+ # Load the level XML
126
+ root = ET.parse(levelfile)
127
+
128
+ # Extract the JSON, and store that
129
+ level_config = extract_level_json(levelfile, root, levels_dirname)
130
+ level['config'] = level_config
131
+
132
+ # Generate the level python, including the toolbox code
133
+ create_maze_python(level_filename, maze_lesson_dirname, level_key, course, lesson, level, root)
134
+
135
+
136
+ if __name__ == "__main__":
137
+ parser = argparse.ArgumentParser(
138
+ prog='Level importer/converter',
139
+ description='Import a level file from code-dot-org and store it locally to the',
140
+ epilog='Text at the bottom of help')
141
+
142
+ parser.add_argument('levelfile', nargs='+',
143
+ help='The file of the level to import and convert')
144
+
145
+ parser.add_argument('-t', '--target_dir', default="levels/", required=False,
146
+ help='Target directory to store the converted files')
147
+
148
+ args = parser.parse_args()
149
+
150
+ create_levels_json(args.levelfile, args.target_dir)
@@ -0,0 +1,278 @@
1
+ import xml.etree.ElementTree as ET
2
+ import json
3
+ import argparse
4
+ import os
5
+ from os.path import join as joinpaths
6
+ import re
7
+ import jinja2
8
+ from . import convert_level
9
+
10
+ def sanitize_filename(filename, replace_spaces=False):
11
+ filename = re.sub(r'[\\/*?:\"<>|]', "-", filename)
12
+ if replace_spaces:
13
+ filename = filename.replace(" ", "_")
14
+ return filename
15
+
16
+
17
+ # Defines the folders to look through
18
+ # TODO Support "turtle" and "flappy"
19
+ SUPPORTED_LEVEL_TYPES = [
20
+ ("maze", joinpaths("levels", "custom", "maze"), "level"),
21
+ ("unplug", joinpaths("levels", "custom", "unplug"), "level"),
22
+ ("standalone_video", joinpaths("levels", "custom", "standalone_video"), "level"),
23
+ ("external", "scripts", "external")
24
+ ]
25
+
26
+
27
+ def find_level_file(levelname, startdir):
28
+ dir_prefix = joinpaths("dashboard", "config")
29
+ levelfile = f"{levelname}"
30
+
31
+ for leveltype in SUPPORTED_LEVEL_TYPES:
32
+ filename = joinpaths(startdir, dir_prefix, leveltype[1], levelfile + "." + leveltype[2])
33
+ if os.path.exists(filename):
34
+ return filename, leveltype[0]
35
+
36
+ return None, None
37
+
38
+
39
+ def find_course_file(coursename, source_dir):
40
+ return joinpaths(source_dir, "dashboard", "config", "scripts_json", coursename + ".script_json")
41
+
42
+
43
+ # Structure of the code-dot-org course script is:
44
+ # - lesson_groups, e.g. High level "Loops" or "Conditionals"
45
+ # - lessons, e.g. "Nested Loops in Maze"
46
+ # - lesson_activities, e.g. "Warm up", "Main Activity", "Wrap Up"
47
+ # - activity_sections, e.g. "Skill Building", "Prediction"
48
+ # - script_levels, e.g. each level in skill building
49
+
50
+
51
+ #
52
+ #
53
+ # An lesson_activity looks like this, and is not needed to generate the puzzle files
54
+ #
55
+ # "key": "8fe0f456-c4bd-4d50-bee3-fa311f5cc147",
56
+ # "position": 1,
57
+ # "properties": {
58
+ # "duration": 10,
59
+ # "name": "Warm Up"
60
+ # },
61
+ # "seeding_key": {
62
+ # "lesson_activity.key": "8fe0f456-c4bd-4d50-bee3-fa311f5cc147",
63
+ # "lesson.key": "Nested Loops in Maze",
64
+ # "lesson_group.key": "csf_loops",
65
+ # "script.name": "coursed-2023"
66
+ # }
67
+ #
68
+ #
69
+ # An activity_section looks like this:
70
+ # {
71
+ # "key": "b9ae7539-4e07-42a1-a318-77b57bcebe7f",
72
+ # "position": 1,
73
+ # "properties": {
74
+ # "description": "These puzzles might sprout some questions, so have the students work in pairs or implement the
75
+ # \"Ask three before you ask me\" rule (have the students ask three other peers for help before they
76
+ # go to the teacher.) This will spark discussions that will develop each student's understanding.",
77
+ # "name": "If/Else with Bee"
78
+ # },
79
+ # "seeding_key": {
80
+ # "activity_section.key": "b9ae7539-4e07-42a1-a318-77b57bcebe7f",
81
+ # "lesson_activity.key": "293c782d-3e53-4092-bea7-c4ed864214b0"
82
+ # }
83
+ # },
84
+
85
+
86
+ # course file e.g.
87
+ # code-dot-org/dashboard/config/scripts_json/coursed-2023.script_json
88
+ # code-dot-org/dashboard/config/levels/custom/maze/courseD_bee_conditionals3_2023.level
89
+
90
+
91
+ # WIP
92
+ # class Course:
93
+ #
94
+ # @staticmethod
95
+ # def from_json(json):
96
+ #
97
+ #
98
+ # def __init__(self, name: str, family_name: str):
99
+ # self.name = name
100
+ # self.family_name = family_name
101
+ # self.dir_name = sanitize_filename(name.lower())
102
+
103
+ def generate_courses(coursename, source_dir, target_dir, maze_dir):
104
+ coursefile = find_course_file(coursename, source_dir)
105
+ print("Found course file", coursefile)
106
+ with open(coursefile) as f:
107
+ course = json.load(f)
108
+ course['key'] = coursename
109
+
110
+ lesson_group_seq = 0
111
+
112
+ # A lesson_group looks like this:
113
+ #
114
+ # "key": "csf_conditionals",
115
+ # "user_facing": true,
116
+ # "position": 5,
117
+ # "properties": {
118
+ # "display_name": "Conditionals"
119
+ # },
120
+ # "seeding_key": {
121
+ # "lesson_group.key": "csf_conditionals",
122
+ # "script.name": "coursed-2023"
123
+ # }
124
+ for lesson_group in course.get("lesson_groups"):
125
+ lesson_group_seq += 1
126
+
127
+ # Get relevant properties from lesson_group
128
+ group_name = lesson_group["properties"]["display_name"]
129
+ lesson_group_key = lesson_group["key"]
130
+
131
+ # Directory name for Group
132
+
133
+ # A lesson looks like this:
134
+ #
135
+ # "key": "If/Else with Bee",
136
+ # "name": "If/Else with Bee",
137
+ # "absolute_position": 14,
138
+ # "lockable": false,
139
+ # "has_lesson_plan": true,
140
+ # "relative_position": 14,
141
+ # "properties": {
142
+ # "creative_commons_license": "Creative Commons BY-NC-SA",
143
+ # "overview": "In this **skill-building** lesson, your class will continue to code with conditionals,
144
+ # allowing them to write code that functions differently depending on the specific
145
+ # conditions the program encounters.",
146
+ # "preparation": " - Play through the puzzles to find any potential problem areas for your class.\n",
147
+ # "purpose": "After being introduced to conditionals in \"Conditionals with Cards,\" students will now
148
+ # practice using them in their programs. The \"if / else\" blocks will allow for a more
149
+ # flexible program. The bee will only collect nectar *if* there is a flower or make honey
150
+ # *if* there is a honeycomb. ",
151
+ # "student_overview": "Now that you understand conditionals, it's time to program Bee to use them when
152
+ # collecting honey and nectar. ",
153
+ # ["unplugged": true] # Only present when unplugged
154
+ # },
155
+ # "seeding_key": {
156
+ # "lesson.key": "If/Else with Bee",
157
+ # "lesson_group.key": "csf_conditionals",
158
+ # "script.name": "coursed-2023"
159
+ # }
160
+ for lesson in course.get("lessons"):
161
+ if lesson["seeding_key"]["lesson_group.key"] == lesson_group_key:
162
+
163
+ props = lesson["properties"]
164
+
165
+ cc_license = props.get("creative_commons_license")
166
+ lesson_seq = lesson["absolute_position"]
167
+ lesson_key = lesson["key"]
168
+ lesson_unplugged = bool(props.get("unplugged"))
169
+
170
+ lesson_name = lesson["name"]
171
+
172
+ # Directory name for the lesson (student repo, puzzle files)
173
+ course_dirname = f"{args.target_dir}/{lesson_group['seeding_key']['script.name']}".lower()
174
+ lesson_dirname = sanitize_filename(f"{lesson_seq} - {group_name} - {lesson_key}")
175
+ lesson_dirname = f"{course_dirname}/{lesson_dirname}"
176
+
177
+ # Mirrored directory name for the lesson (maze repo, test_ solution files only)
178
+ maze_course_dirname = f"{args.maze_dir}/{lesson_group['seeding_key']['script.name']}".lower()
179
+ maze_lesson_dirname = f"{maze_course_dirname}/{sanitize_filename(f'{lesson_seq} - {group_name} - {lesson_key}')}"
180
+
181
+ # Shared flat folder for all packaged level json files, in the maze repo
182
+ maze_levels_dirname = f"{args.maze_dir}/src/maze/levels"
183
+
184
+ # Only continue if the lesson is under Creative Commons license
185
+ if cc_license:
186
+ os.makedirs(lesson_dirname, exist_ok=True)
187
+
188
+ # TODO Make Lesson MD file for the lesson
189
+
190
+ # A script_level looks like this:
191
+ #
192
+ # "chapter": 120,
193
+ # "position": 4,
194
+ # "activity_section_position": 2,
195
+ # "assessment": false,
196
+ # "properties": {
197
+ # "level_keys": [
198
+ # "courseD_bee_conditionals3_2023"
199
+ # ],
200
+ # "progression": "Skill Building"
201
+ # },
202
+ # "bonus": false,
203
+ # "seeding_key": {
204
+ # "script_level.level_keys": [
205
+ # "courseD_bee_conditionals3_2023"
206
+ # ],
207
+ # "lesson.key": "If/Else with Bee",
208
+ # "lesson_group.key": "csf_conditionals",
209
+ # "script.name": "coursed-2023",
210
+ # "activity_section.key": "c3e5d3a5-f776-4bd6-93d6-821025f2c40f"
211
+ # },
212
+ # "level_keys": [
213
+ # "courseD_bee_conditionals3_2023"
214
+ # ]
215
+ for level in course.get("script_levels"):
216
+ props = level["properties"]
217
+ level_key:str = level["level_keys"][0] # TODO, check if there could ever be more
218
+ level_seq = level["position"]
219
+ level_progression = props.get("progression")
220
+ section_pos = level["activity_section_position"]
221
+
222
+ if lesson_key == level["seeding_key"]["lesson.key"]:
223
+ levelfile, leveltype = find_level_file(level_key, source_dir)
224
+
225
+ if not levelfile:
226
+ print(f"No levefile foud for: {level_key}")
227
+ continue
228
+
229
+ if level_progression:
230
+ level_progression = sanitize_filename(level_progression)
231
+ level_filename = f"{lesson_dirname}/{level_seq}_{level_progression}_{section_pos}"
232
+ else:
233
+ predict = "_predict" if "predict" in levelfile else ""
234
+ level_filename = f"{lesson_dirname}/{level_seq}_{leveltype}{predict}"
235
+
236
+ if leveltype == 'maze':
237
+ convert_level.handle_level(levelfile, leveltype, maze_levels_dirname, maze_lesson_dirname, level_filename, level_key, course, lesson, level)
238
+
239
+ elif leveltype == 'unplug':
240
+ print("unplug levels not yet implemented")
241
+ elif leveltype == 'standalone_video':
242
+ print("standalone_video levels not yet implemented")
243
+ elif leveltype == 'external':
244
+ print("external levels not yet implemented")
245
+
246
+ else:
247
+ print(f"Skipping lesson '{lesson_name}' as it does not have a creative_commons_license")
248
+
249
+ if os.path.isdir(lesson_dirname) and not bool(os.listdir(lesson_dirname)):
250
+ # If the directory exist and is empty after looping through all the levels, remove it
251
+ os.rmdir(lesson_dirname)
252
+
253
+ if os.path.isdir(maze_lesson_dirname) and not bool(os.listdir(maze_lesson_dirname)):
254
+ os.rmdir(maze_lesson_dirname)
255
+
256
+
257
+
258
+ if __name__ == "__main__":
259
+ parser = argparse.ArgumentParser(
260
+ prog='Course importer/converter',
261
+ description='Import a course file from code-dot-org and create empty puzzle files',
262
+ epilog='example: \ncreate_course -s ../code-dot-org -t ../code-org-python -m . coursed-2023')
263
+
264
+ parser.add_argument('-s', '--source_dir',
265
+ help='The root folder for code-dot-org codebase, to look for courses within')
266
+
267
+ parser.add_argument('-t', '--target_dir', default="course/", required=False,
268
+ help='Target directory of the student-facing repo to store the puzzle files in')
269
+
270
+ parser.add_argument('-m', '--maze_dir', required=True,
271
+ help='Root directory of the maze repo, to store level json and test_ solution files in')
272
+
273
+ parser.add_argument('coursename',
274
+ help='The name of the course to import and convert')
275
+
276
+ args = parser.parse_args()
277
+
278
+ generate_courses(args.coursename, args.source_dir, args.target_dir, args.maze_dir)
importer/lesson.md.j2 ADDED
@@ -0,0 +1,3 @@
1
+ # {lesson.lesson_name}
2
+
3
+ {lesson.student_overview}
importer/maze.py.j2 ADDED
@@ -0,0 +1,20 @@
1
+ from maze import Puzzle, {{player_type}}
2
+
3
+ maze = Puzzle.from_file("{{levelname}}")
4
+ {{player}}: {{player_type}} = maze.player
5
+
6
+ '''
7
+ https://studio.code.org/s/{{course.key}}/lessons/{{lesson.absolute_position}}/levels/{{level.position}}
8
+
9
+ {{level.config.properties.long_instructions}}
10
+
11
+ ---
12
+ {{toolbox}}
13
+ '''
14
+
15
+ # When run
16
+
17
+ {{start_code}}
18
+
19
+ # Keep this
20
+ Puzzle.done()
importer/test.py ADDED
@@ -0,0 +1,24 @@
1
+ #from code_blocks import generate_python_from_blocks, generate_toolbox_python
2
+ import xml.etree.ElementTree as ET
3
+ import os
4
+ #
5
+ #
6
+ # # root = ET.parse("/Users/sgrinsted/ws/codingpirates/code-dot-org/dashboard/config/levels/custom/maze/courseD_farmer_while_challenge1_2023.level")
7
+ # root = ET.parse("/Users/sgrinsted/ws/codingpirates/code-dot-org/dashboard/config/levels/custom/maze/courseD_maze_until10_2023.level")
8
+ #
9
+ # solution = root.find("./blocks/solution_blocks/xml")
10
+ #
11
+ # code = generate_python_from_blocks(solution, player="zombie")
12
+ #
13
+ # print(code)
14
+
15
+
16
+ def chorus():
17
+ print("doo doo, doo doo doo doo")
18
+
19
+
20
+ for person in ("Baby", "Mommy"):
21
+ for i in range(4):
22
+ print(person + " Shark")
23
+ chorus()
24
+
importer/test_all.py ADDED
@@ -0,0 +1,24 @@
1
+ import subprocess
2
+ import os
3
+
4
+ def run_script(script_path):
5
+ env = os.environ.copy()
6
+ # Add your custom environment variable
7
+ env["TESTMODE"] = "True"
8
+
9
+ result = subprocess.run(['python', script_path], env=env, stdout=subprocess.PIPE)
10
+ output = result.stdout.decode('utf-8').strip()
11
+
12
+ print(f"{output} {script_path}")
13
+
14
+ def visit_dir(dir):
15
+ for file in os.listdir(dir):
16
+ if os.path.isdir(file):
17
+ visit_dir(os.path.join(dir, file))
18
+ elif file.endswith(".py") and file != "test_all.py":
19
+ run_script(os.path.join(dir, file))
20
+
21
+
22
+ if __name__ == "__main__":
23
+ visit_dir(".")
24
+ print("DONE")
maze/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ from .maze import Maze, Player, Cell, SquareType
2
+ from .bee import BeeMazeType, BeeFeatureType, BeeCell, BeePlayer, CloudType, FlowerColor
3
+ from .pvz import ZombieMazeType, ZombieCell, ZombiePlayer
4
+ from .farmer import FarmerMazeType, FarmerCell, Farmer
5
+ from .harvester import HarvesterMazeType, HarvesterCell, Harvester
6
+ from .birds import BirdsMazeType, BirdsCell, Bird
7
+ from .collector import CollectorMazeType, CollectorCell, CollectorPath, Collector
8
+ from .puzzle import Puzzle
9
+ from .screencapture import capture