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.
- codingpirates_maze-0.1.0.dist-info/METADATA +105 -0
- codingpirates_maze-0.1.0.dist-info/RECORD +539 -0
- codingpirates_maze-0.1.0.dist-info/WHEEL +4 -0
- codingpirates_maze-0.1.0.dist-info/licenses/LICENSE +201 -0
- importer/code_blocks.py +339 -0
- importer/convert_level.py +150 -0
- importer/create_course.py +278 -0
- importer/lesson.md.j2 +3 -0
- importer/maze.py.j2 +20 -0
- importer/test.py +24 -0
- importer/test_all.py +24 -0
- maze/__init__.py +9 -0
- maze/bee.py +184 -0
- maze/beeshape.py +115 -0
- maze/birds.py +69 -0
- maze/collector.py +106 -0
- maze/farmer.py +95 -0
- maze/harvester.py +141 -0
- maze/images/bee/background.png +0 -0
- maze/images/bee/cloud.gif +0 -0
- maze/images/bee/honeycomb.gif +0 -0
- maze/images/bee/path.gif +0 -0
- maze/images/bee/purple_flower.gif +0 -0
- maze/images/bee/red_flower.gif +0 -0
- maze/images/birds/.gitignore +2 -0
- maze/images/birds/background.png +0 -0
- maze/images/birds/monster.gif +0 -0
- maze/images/birds/path.gif +0 -0
- maze/images/birds/tnt.gif +0 -0
- maze/images/birds/wall_1.gif +0 -0
- maze/images/birds/wall_2.gif +0 -0
- maze/images/birds/wall_3.gif +0 -0
- maze/images/birds/wall_4.gif +0 -0
- maze/images/birds/wall_5.gif +0 -0
- maze/images/collector/.gitignore +1 -0
- maze/images/collector/crystal.gif +0 -0
- maze/images/collector/floor_1.gif +0 -0
- maze/images/collector/floor_2.gif +0 -0
- maze/images/collector/floor_3.gif +0 -0
- maze/images/collector/floor_4.gif +0 -0
- maze/images/collector/wall_1.gif +0 -0
- maze/images/collector/wall_2.gif +0 -0
- maze/images/farmer/background.png +0 -0
- maze/images/farmer/corn.gif +0 -0
- maze/images/farmer/hole.gif +0 -0
- maze/images/farmer/lettuce.gif +0 -0
- maze/images/farmer/path.gif +0 -0
- maze/images/farmer/pile.gif +0 -0
- maze/images/farmer/pumpkin.gif +0 -0
- maze/images/farmer/sprout.gif +0 -0
- maze/images/pvz/background.gif +0 -0
- maze/images/pvz/flower.gif +0 -0
- maze/images/pvz/obstacle.gif +0 -0
- maze/images/pvz/path.gif +0 -0
- maze/images/pvz/pumpkin.gif +0 -0
- maze/levels/CourseD_2022LessonExtra14a_2023.json +46 -0
- maze/levels/CourseD_2022LessonExtra14a_2024.json +46 -0
- maze/levels/CourseD_2022LessonExtra14a_2025.json +46 -0
- maze/levels/CourseD_2022LessonExtra15a_2023.json +46 -0
- maze/levels/CourseD_2022LessonExtra15a_2024.json +46 -0
- maze/levels/CourseD_2022LessonExtra15a_2025.json +46 -0
- maze/levels/CourseD_2022LessonExtra16a_2023.json +46 -0
- maze/levels/CourseD_2022LessonExtra16a_2024.json +46 -0
- maze/levels/CourseD_2022LessonExtra16a_2025.json +46 -0
- maze/levels/CourseD_2022_LessonExtra14b_2023.json +47 -0
- maze/levels/CourseD_2022_LessonExtra14b_2024.json +47 -0
- maze/levels/CourseD_2022_LessonExtra14b_2025.json +47 -0
- maze/levels/CourseD_2022_LessonExtra15b_2023.json +47 -0
- maze/levels/CourseD_2022_LessonExtra15b_2024.json +47 -0
- maze/levels/CourseD_2022_LessonExtra15b_2025.json +47 -0
- maze/levels/CourseD_2022_LessonExtra16b_2023.json +47 -0
- maze/levels/CourseD_2022_LessonExtra16b_2024.json +47 -0
- maze/levels/CourseD_2022_LessonExtra16b_2025.json +47 -0
- maze/levels/courseA_harvester_loops10_2024.json +51 -0
- maze/levels/courseA_harvester_loops11_2024.json +51 -0
- maze/levels/courseA_harvester_loops1_2024.json +50 -0
- maze/levels/courseA_harvester_loops2_2024.json +54 -0
- maze/levels/courseA_harvester_loops3_2024.json +57 -0
- maze/levels/courseA_harvester_loops4_2024.json +52 -0
- maze/levels/courseA_harvester_loops5_2024.json +53 -0
- maze/levels/courseA_harvester_loops5a_2024.json +53 -0
- maze/levels/courseA_harvester_loops5b_2024.json +52 -0
- maze/levels/courseA_harvester_loops6_2024.json +55 -0
- maze/levels/courseA_harvester_loops7_2024.json +53 -0
- maze/levels/courseA_harvester_loops9_2024.json +51 -0
- maze/levels/courseA_harvester_seq10_2024.json +50 -0
- maze/levels/courseA_harvester_seq11_2024.json +50 -0
- maze/levels/courseA_harvester_seq12_2024.json +50 -0
- maze/levels/courseA_harvester_seq13_2024.json +50 -0
- maze/levels/courseA_harvester_seq1_2024.json +49 -0
- maze/levels/courseA_harvester_seq2_2024.json +54 -0
- maze/levels/courseA_harvester_seq3_2024.json +54 -0
- maze/levels/courseA_harvester_seq4_2024.json +51 -0
- maze/levels/courseA_harvester_seq5_2024.json +51 -0
- maze/levels/courseA_harvester_seq6_2024.json +50 -0
- maze/levels/courseA_harvester_seq7_2024.json +51 -0
- maze/levels/courseA_harvester_seq8_2024.json +51 -0
- maze/levels/courseA_harvester_seq9_2024.json +51 -0
- maze/levels/courseA_maze_ramp2_2024.json +53 -0
- maze/levels/courseA_maze_ramp3a_2024.json +53 -0
- maze/levels/courseA_maze_ramp3b_2024.json +50 -0
- maze/levels/courseA_maze_ramp4a_2024.json +48 -0
- maze/levels/courseA_maze_ramp5a_2024.json +47 -0
- maze/levels/courseA_maze_seq10_2024.json +48 -0
- maze/levels/courseA_maze_seq11_2024.json +47 -0
- maze/levels/courseA_maze_seq12_2024.json +47 -0
- maze/levels/courseA_maze_seq13_2024.json +49 -0
- maze/levels/courseA_maze_seq1_2024.json +52 -0
- maze/levels/courseA_maze_seq5_2024.json +47 -0
- maze/levels/courseA_maze_seq5a_2024.json +47 -0
- maze/levels/courseA_maze_seq6_2024.json +47 -0
- maze/levels/courseA_maze_seq8_2024.json +47 -0
- maze/levels/courseA_maze_seq_challenge1_2024.json +45 -0
- maze/levels/courseA_maze_seq_challenge2_2024.json +50 -0
- maze/levels/courseB_collector_loops10_2024.json +53 -0
- maze/levels/courseB_collector_loops11_2024.json +51 -0
- maze/levels/courseB_collector_loops1_2024.json +53 -0
- maze/levels/courseB_collector_loops2_2024.json +53 -0
- maze/levels/courseB_collector_loops3_2024.json +57 -0
- maze/levels/courseB_collector_loops4_2024.json +57 -0
- maze/levels/courseB_collector_loops5_2024.json +54 -0
- maze/levels/courseB_collector_loops6_2024.json +54 -0
- maze/levels/courseB_collector_loops7_2024.json +53 -0
- maze/levels/courseB_collector_loops8_2024.json +54 -0
- maze/levels/courseB_collector_loops9_2024.json +54 -0
- maze/levels/courseB_collector_loops_challenge1_2024.json +54 -0
- maze/levels/courseB_collector_loops_challenge2a_2024.json +51 -0
- maze/levels/courseC_collector_loops_challenge2_2024.json +53 -0
- maze/levels/courseC_collector_prog1_2023.json +52 -0
- maze/levels/courseC_collector_prog1_2024.json +52 -0
- maze/levels/courseC_collector_prog2_2023.json +53 -0
- maze/levels/courseC_collector_prog2_2024.json +53 -0
- maze/levels/courseC_collector_prog3_2023.json +49 -0
- maze/levels/courseC_collector_prog3_2024.json +49 -0
- maze/levels/courseC_collector_prog4_2023.json +50 -0
- maze/levels/courseC_collector_prog4_2024.json +50 -0
- maze/levels/courseC_collector_prog5_2023.json +50 -0
- maze/levels/courseC_collector_prog5_2024.json +50 -0
- maze/levels/courseC_collector_prog6_2023.json +50 -0
- maze/levels/courseC_collector_prog6_2024.json +50 -0
- maze/levels/courseC_collector_prog7_2023.json +51 -0
- maze/levels/courseC_collector_prog7_2024.json +51 -0
- maze/levels/courseC_collector_prog8_2023.json +52 -0
- maze/levels/courseC_collector_prog8_2024.json +52 -0
- maze/levels/courseC_collector_prog9_2023.json +52 -0
- maze/levels/courseC_collector_prog9_2024.json +52 -0
- maze/levels/courseC_collector_prog_challenge1_2023.json +51 -0
- maze/levels/courseC_collector_prog_challenge1_2024.json +51 -0
- maze/levels/courseC_collector_prog_challenge2_2023.json +51 -0
- maze/levels/courseC_collector_prog_challenge2_2024.json +51 -0
- maze/levels/courseC_harvester_loops10_2024.json +50 -0
- maze/levels/courseC_harvester_loops11_2024.json +50 -0
- maze/levels/courseC_harvester_loops11_predict1_2024.json +47 -0
- maze/levels/courseC_harvester_loops1_2024.json +52 -0
- maze/levels/courseC_harvester_loops2_2024.json +50 -0
- maze/levels/courseC_harvester_loops3_2024.json +50 -0
- maze/levels/courseC_harvester_loops4_2024.json +50 -0
- maze/levels/courseC_harvester_loops5_2024.json +50 -0
- maze/levels/courseC_harvester_loops6_2024.json +50 -0
- maze/levels/courseC_harvester_loops7_2024.json +50 -0
- maze/levels/courseC_harvester_loops8_2024.json +49 -0
- maze/levels/courseC_harvester_loops9_2024.json +50 -0
- maze/levels/courseC_harvester_loops_challenge1a_2024.json +50 -0
- maze/levels/courseC_harvester_loops_challenge2_2024.json +48 -0
- maze/levels/courseC_maze_debugging1.json +47 -0
- maze/levels/courseC_maze_debugging2.json +44 -0
- maze/levels/courseC_maze_debugging3.json +46 -0
- maze/levels/courseC_maze_debugging4.json +44 -0
- maze/levels/courseC_maze_debugging5.json +46 -0
- maze/levels/courseC_maze_debugging6.json +44 -0
- maze/levels/courseC_maze_debugging7.json +45 -0
- maze/levels/courseC_maze_debugging8_predict1.json +43 -0
- maze/levels/courseC_maze_debugging9.json +44 -0
- maze/levels/courseC_maze_loops_challenge1a_2024.json +47 -0
- maze/levels/courseC_maze_programming1_2023.json +50 -0
- maze/levels/courseC_maze_programming1_2024.json +50 -0
- maze/levels/courseC_maze_programming2_2023.json +53 -0
- maze/levels/courseC_maze_programming2_2024.json +53 -0
- maze/levels/courseC_maze_programming3_2023.json +47 -0
- maze/levels/courseC_maze_programming3_2024.json +47 -0
- maze/levels/courseC_maze_programming4_2023.json +46 -0
- maze/levels/courseC_maze_programming4_2024.json +46 -0
- maze/levels/courseC_maze_programming5_2023.json +46 -0
- maze/levels/courseC_maze_programming5_2024.json +46 -0
- maze/levels/courseC_maze_programming6_2023.json +50 -0
- maze/levels/courseC_maze_programming6_2024.json +49 -0
- maze/levels/courseC_maze_programming7_2023.json +54 -0
- maze/levels/courseC_maze_programming7_2024.json +54 -0
- maze/levels/courseC_maze_programming8_2023.json +52 -0
- maze/levels/courseC_maze_programming8_2024.json +51 -0
- maze/levels/courseC_maze_programming8_predict1_2024.json +45 -0
- maze/levels/courseC_maze_programming9_2023.json +47 -0
- maze/levels/courseC_maze_programming9_2024.json +47 -0
- maze/levels/courseC_maze_programming_challenge1_2023.json +47 -0
- maze/levels/courseC_maze_programming_challenge1_2024.json +47 -0
- maze/levels/courseC_maze_programming_challenge2_2023.json +49 -0
- maze/levels/courseC_maze_programming_challenge2_2024.json +49 -0
- maze/levels/courseD_bee_conditionals10_2023.json +57 -0
- maze/levels/courseD_bee_conditionals10_2024.json +57 -0
- maze/levels/courseD_bee_conditionals10_2025.json +57 -0
- maze/levels/courseD_bee_conditionals1_predict1_2023.json +51 -0
- maze/levels/courseD_bee_conditionals1_predict1_2024.json +51 -0
- maze/levels/courseD_bee_conditionals1_predict1_2025.json +51 -0
- maze/levels/courseD_bee_conditionals2_2023.json +60 -0
- maze/levels/courseD_bee_conditionals2_2024.json +60 -0
- maze/levels/courseD_bee_conditionals2_2025.json +60 -0
- maze/levels/courseD_bee_conditionals3_2023.json +57 -0
- maze/levels/courseD_bee_conditionals3_2024.json +57 -0
- maze/levels/courseD_bee_conditionals3_2025.json +57 -0
- maze/levels/courseD_bee_conditionals4_2023.json +58 -0
- maze/levels/courseD_bee_conditionals4_2024.json +58 -0
- maze/levels/courseD_bee_conditionals4_2025.json +58 -0
- maze/levels/courseD_bee_conditionals5_2023.json +58 -0
- maze/levels/courseD_bee_conditionals5_2024.json +58 -0
- maze/levels/courseD_bee_conditionals5_2025.json +58 -0
- maze/levels/courseD_bee_conditionals6_2023.json +57 -0
- maze/levels/courseD_bee_conditionals6_2024.json +57 -0
- maze/levels/courseD_bee_conditionals6_2025.json +57 -0
- maze/levels/courseD_bee_conditionals7_2023.json +57 -0
- maze/levels/courseD_bee_conditionals7_2024.json +57 -0
- maze/levels/courseD_bee_conditionals7_2025.json +57 -0
- maze/levels/courseD_bee_conditionals7_predict2_2023.json +54 -0
- maze/levels/courseD_bee_conditionals7_predict2_2024.json +54 -0
- maze/levels/courseD_bee_conditionals7_predict2_2025.json +54 -0
- maze/levels/courseD_bee_conditionals8_2023.json +57 -0
- maze/levels/courseD_bee_conditionals8_2024.json +57 -0
- maze/levels/courseD_bee_conditionals8_2025.json +57 -0
- maze/levels/courseD_bee_conditionals9_2023.json +55 -0
- maze/levels/courseD_bee_conditionals9_2024.json +55 -0
- maze/levels/courseD_bee_conditionals9_2025.json +55 -0
- maze/levels/courseD_bee_conditionals_challenge1_2023.json +57 -0
- maze/levels/courseD_bee_conditionals_challenge1_2024.json +57 -0
- maze/levels/courseD_bee_conditionals_challenge1_2025.json +57 -0
- maze/levels/courseD_bee_conditionals_challenge2_2023.json +58 -0
- maze/levels/courseD_bee_conditionals_challenge2_2024.json +58 -0
- maze/levels/courseD_bee_conditionals_challenge2_2025.json +58 -0
- maze/levels/courseD_bee_nestedLoops1.json +49 -0
- maze/levels/courseD_bee_nestedLoops1_2023.json +53 -0
- maze/levels/courseD_bee_nestedLoops1_2024.json +53 -0
- maze/levels/courseD_bee_nestedLoops1_2025.json +53 -0
- maze/levels/courseD_bee_nestedLoops1a.json +49 -0
- maze/levels/courseD_bee_nestedLoops1a_2023.json +55 -0
- maze/levels/courseD_bee_nestedLoops1a_2024.json +55 -0
- maze/levels/courseD_bee_nestedLoops1a_2025.json +55 -0
- maze/levels/courseD_bee_nestedLoops2.json +50 -0
- maze/levels/courseD_bee_nestedLoops2_2023.json +57 -0
- maze/levels/courseD_bee_nestedLoops2_2024.json +57 -0
- maze/levels/courseD_bee_nestedLoops2_2025.json +57 -0
- maze/levels/courseD_bee_nestedLoops2_predict1.json +51 -0
- maze/levels/courseD_bee_nestedLoops2_predict1_2023.json +51 -0
- maze/levels/courseD_bee_nestedLoops2_predict1_2024.json +51 -0
- maze/levels/courseD_bee_nestedLoops2_predict1_2025.json +51 -0
- maze/levels/courseD_bee_nestedLoops7.json +53 -0
- maze/levels/courseD_bee_nestedLoops7_2023.json +59 -0
- maze/levels/courseD_bee_nestedLoops7_2024.json +59 -0
- maze/levels/courseD_bee_nestedLoops7_2025.json +59 -0
- maze/levels/courseD_bee_nestedLoops8.json +52 -0
- maze/levels/courseD_bee_nestedLoops8_2023.json +59 -0
- maze/levels/courseD_bee_nestedLoops8_2024.json +59 -0
- maze/levels/courseD_bee_nestedLoops8_2025.json +59 -0
- maze/levels/courseD_bee_nestedLoops9.json +53 -0
- maze/levels/courseD_bee_nestedLoops9_2023.json +60 -0
- maze/levels/courseD_bee_nestedLoops9_2024.json +60 -0
- maze/levels/courseD_bee_nestedLoops9_2025.json +60 -0
- maze/levels/courseD_bee_nestedLoops9_predict2.json +51 -0
- maze/levels/courseD_bee_nestedLoops9_predict2_2023.json +54 -0
- maze/levels/courseD_bee_nestedLoops9_predict2_2024.json +54 -0
- maze/levels/courseD_bee_nestedLoops9_predict2_2025.json +54 -0
- maze/levels/courseD_collector_debugging1a_2023.json +50 -0
- maze/levels/courseD_collector_debugging1a_2024.json +50 -0
- maze/levels/courseD_collector_debugging1a_2025.json +50 -0
- maze/levels/courseD_collector_debugging2a_2023.json +50 -0
- maze/levels/courseD_collector_debugging2a_2024.json +50 -0
- maze/levels/courseD_collector_debugging2a_2025.json +50 -0
- maze/levels/courseD_collector_debugging3a_2023.json +50 -0
- maze/levels/courseD_collector_debugging3a_2024.json +50 -0
- maze/levels/courseD_collector_debugging3a_2025.json +50 -0
- maze/levels/courseD_collector_debugging4a_2023.json +50 -0
- maze/levels/courseD_collector_debugging4a_2024.json +50 -0
- maze/levels/courseD_collector_debugging4a_2025.json +50 -0
- maze/levels/courseD_collector_debugging5a_2023.json +50 -0
- maze/levels/courseD_collector_debugging5a_2024.json +50 -0
- maze/levels/courseD_collector_debugging5a_2025.json +50 -0
- maze/levels/courseD_collector_debugging6a_2023.json +56 -0
- maze/levels/courseD_collector_debugging6a_2024.json +56 -0
- maze/levels/courseD_collector_debugging6a_2025.json +56 -0
- maze/levels/courseD_collector_debugging8a_2023.json +49 -0
- maze/levels/courseD_collector_debugging8a_2024.json +49 -0
- maze/levels/courseD_collector_debugging8a_2025.json +49 -0
- maze/levels/courseD_collector_debugging9a_2023.json +49 -0
- maze/levels/courseD_collector_debugging9a_2024.json +49 -0
- maze/levels/courseD_collector_debugging9a_2025.json +49 -0
- maze/levels/courseD_collector_nested_loops_challenge2_2024.json +50 -0
- maze/levels/courseD_collector_nested_loops_challenge2_2025.json +50 -0
- maze/levels/courseD_farmer_while10_2023.json +50 -0
- maze/levels/courseD_farmer_while10_2024.json +50 -0
- maze/levels/courseD_farmer_while10_2025.json +50 -0
- maze/levels/courseD_farmer_while10_predict2_2024.json +45 -0
- maze/levels/courseD_farmer_while10_predict2_2025.json +45 -0
- maze/levels/courseD_farmer_while1_2023.json +49 -0
- maze/levels/courseD_farmer_while1_2024.json +49 -0
- maze/levels/courseD_farmer_while1_2025.json +49 -0
- maze/levels/courseD_farmer_while2_2023.json +45 -0
- maze/levels/courseD_farmer_while2_2024.json +45 -0
- maze/levels/courseD_farmer_while2_2025.json +45 -0
- maze/levels/courseD_farmer_while3_2023.json +47 -0
- maze/levels/courseD_farmer_while3_2024.json +47 -0
- maze/levels/courseD_farmer_while3_2025.json +47 -0
- maze/levels/courseD_farmer_while4_2023.json +50 -0
- maze/levels/courseD_farmer_while4_2024.json +50 -0
- maze/levels/courseD_farmer_while4_2025.json +50 -0
- maze/levels/courseD_farmer_while4_predict1_2024.json +47 -0
- maze/levels/courseD_farmer_while4_predict1_2025.json +47 -0
- maze/levels/courseD_farmer_while5_2023.json +52 -0
- maze/levels/courseD_farmer_while5_2024.json +52 -0
- maze/levels/courseD_farmer_while5_2025.json +52 -0
- maze/levels/courseD_farmer_while6_2023.json +51 -0
- maze/levels/courseD_farmer_while6_2024.json +51 -0
- maze/levels/courseD_farmer_while6_2025.json +51 -0
- maze/levels/courseD_farmer_while7_2023.json +52 -0
- maze/levels/courseD_farmer_while7_2024.json +52 -0
- maze/levels/courseD_farmer_while7_2025.json +52 -0
- maze/levels/courseD_farmer_while8_2023.json +51 -0
- maze/levels/courseD_farmer_while8_2024.json +51 -0
- maze/levels/courseD_farmer_while8_2025.json +51 -0
- maze/levels/courseD_farmer_while9_2023.json +50 -0
- maze/levels/courseD_farmer_while9_2024.json +50 -0
- maze/levels/courseD_farmer_while9_2025.json +50 -0
- maze/levels/courseD_farmer_while_challenge1_2023.json +50 -0
- maze/levels/courseD_farmer_while_challenge1_2024.json +50 -0
- maze/levels/courseD_farmer_while_challenge1_2025.json +50 -0
- maze/levels/courseD_harvester_nested_loops_challenge1_2024.json +51 -0
- maze/levels/courseD_harvester_nested_loops_challenge1_2025.json +51 -0
- maze/levels/courseD_iceage_loops10_2024.json +48 -0
- maze/levels/courseD_iceage_loops10_2025.json +48 -0
- maze/levels/courseD_iceage_loops11_2024.json +46 -0
- maze/levels/courseD_iceage_loops11_2025.json +46 -0
- maze/levels/courseD_iceage_loops12_2024.json +46 -0
- maze/levels/courseD_iceage_loops12_2025.json +46 -0
- maze/levels/courseD_iceage_loops1_2024.json +49 -0
- maze/levels/courseD_iceage_loops1_2025.json +49 -0
- maze/levels/courseD_iceage_loops2_2024.json +51 -0
- maze/levels/courseD_iceage_loops2_2025.json +51 -0
- maze/levels/courseD_iceage_loops3_2024.json +51 -0
- maze/levels/courseD_iceage_loops3_2025.json +51 -0
- maze/levels/courseD_iceage_loops4_2024.json +47 -0
- maze/levels/courseD_iceage_loops4_2025.json +47 -0
- maze/levels/courseD_iceage_loops6_2024.json +47 -0
- maze/levels/courseD_iceage_loops6_2025.json +47 -0
- maze/levels/courseD_iceage_loops7_2024.json +47 -0
- maze/levels/courseD_iceage_loops7_2025.json +47 -0
- maze/levels/courseD_iceage_loops8_2024.json +49 -0
- maze/levels/courseD_iceage_loops8_2025.json +49 -0
- maze/levels/courseD_iceage_loops9_2024.json +49 -0
- maze/levels/courseD_iceage_loops9_2025.json +49 -0
- maze/levels/courseD_maze_intro4_2023.json +45 -0
- maze/levels/courseD_maze_intro4_2024.json +45 -0
- maze/levels/courseD_maze_intro4_2025.json +45 -0
- maze/levels/courseD_maze_intro5a_2023.json +45 -0
- maze/levels/courseD_maze_intro5a_2024.json +45 -0
- maze/levels/courseD_maze_intro5a_2025.json +45 -0
- maze/levels/courseD_maze_intro5c_2023.json +45 -0
- maze/levels/courseD_maze_intro5c_2024.json +45 -0
- maze/levels/courseD_maze_intro5c_2025.json +45 -0
- maze/levels/courseD_maze_nestedLoops3.json +46 -0
- maze/levels/courseD_maze_nestedLoops3_2023.json +47 -0
- maze/levels/courseD_maze_nestedLoops3_2024.json +47 -0
- maze/levels/courseD_maze_nestedLoops3_2025.json +47 -0
- maze/levels/courseD_maze_nestedLoops4.json +45 -0
- maze/levels/courseD_maze_nestedLoops4_2023.json +47 -0
- maze/levels/courseD_maze_nestedLoops4_2024.json +47 -0
- maze/levels/courseD_maze_nestedLoops4_2025.json +47 -0
- maze/levels/courseD_maze_nestedLoops5.json +44 -0
- maze/levels/courseD_maze_nestedLoops5_2023.json +45 -0
- maze/levels/courseD_maze_nestedLoops5_2024.json +45 -0
- maze/levels/courseD_maze_nestedLoops5_2025.json +45 -0
- maze/levels/courseD_maze_nestedLoops6.json +43 -0
- maze/levels/courseD_maze_nestedLoops6_2023.json +45 -0
- maze/levels/courseD_maze_nestedLoops6_2024.json +45 -0
- maze/levels/courseD_maze_nestedLoops6_2025.json +45 -0
- maze/levels/courseD_maze_ramp1.json +47 -0
- maze/levels/courseD_maze_ramp1_2023.json +45 -0
- maze/levels/courseD_maze_ramp1_2024.json +45 -0
- maze/levels/courseD_maze_ramp1_2025.json +45 -0
- maze/levels/courseD_maze_ramp2.json +47 -0
- maze/levels/courseD_maze_ramp2_2023.json +45 -0
- maze/levels/courseD_maze_ramp2_2024.json +45 -0
- maze/levels/courseD_maze_ramp2_2025.json +45 -0
- maze/levels/courseD_maze_ramp3.json +46 -0
- maze/levels/courseD_maze_ramp3_2023.json +45 -0
- maze/levels/courseD_maze_ramp3_2024.json +45 -0
- maze/levels/courseD_maze_ramp3_2025.json +45 -0
- maze/levels/courseD_maze_ramp4.json +47 -0
- maze/levels/courseD_maze_ramp5.json +44 -0
- maze/levels/courseD_maze_ramp5_2023.json +42 -0
- maze/levels/courseD_maze_ramp5_2024.json +42 -0
- maze/levels/courseD_maze_ramp5_2025.json +42 -0
- maze/levels/courseD_maze_ramp5a.json +45 -0
- maze/levels/courseD_maze_ramp5b.json +45 -0
- maze/levels/courseD_maze_ramp5b_2023.json +43 -0
- maze/levels/courseD_maze_ramp5b_2024.json +43 -0
- maze/levels/courseD_maze_ramp5b_2025.json +43 -0
- maze/levels/courseD_maze_ramp5c.json +46 -0
- maze/levels/courseD_maze_ramp5d.json +45 -0
- maze/levels/courseD_maze_ramp5d_2023.json +43 -0
- maze/levels/courseD_maze_ramp5d_2024.json +43 -0
- maze/levels/courseD_maze_ramp5d_2025.json +43 -0
- maze/levels/courseD_maze_until10_2023.json +53 -0
- maze/levels/courseD_maze_until10_2024.json +53 -0
- maze/levels/courseD_maze_until10_2025.json +53 -0
- maze/levels/courseD_maze_until10_predict2_2024.json +52 -0
- maze/levels/courseD_maze_until10_predict2_2025.json +52 -0
- maze/levels/courseD_maze_until1_2023.json +47 -0
- maze/levels/courseD_maze_until1_2024.json +47 -0
- maze/levels/courseD_maze_until1_2025.json +47 -0
- maze/levels/courseD_maze_until2_predict1_2024.json +44 -0
- maze/levels/courseD_maze_until2_predict1_2025.json +44 -0
- maze/levels/courseD_maze_until3_2023.json +48 -0
- maze/levels/courseD_maze_until3_2024.json +48 -0
- maze/levels/courseD_maze_until3_2025.json +48 -0
- maze/levels/courseD_maze_until4_2023.json +49 -0
- maze/levels/courseD_maze_until4_2024.json +49 -0
- maze/levels/courseD_maze_until4_2025.json +49 -0
- maze/levels/courseD_maze_until5_2023.json +49 -0
- maze/levels/courseD_maze_until5_2024.json +49 -0
- maze/levels/courseD_maze_until5_2025.json +49 -0
- maze/levels/courseD_maze_until6_2023.json +49 -0
- maze/levels/courseD_maze_until6_2024.json +49 -0
- maze/levels/courseD_maze_until6_2025.json +49 -0
- maze/levels/courseD_maze_until7_2023.json +49 -0
- maze/levels/courseD_maze_until7_2024.json +49 -0
- maze/levels/courseD_maze_until7_2025.json +49 -0
- maze/levels/courseD_maze_until9_2023.json +53 -0
- maze/levels/courseD_maze_until9_2024.json +53 -0
- maze/levels/courseD_maze_until9_2025.json +53 -0
- maze/levels/courseD_scrat_predict1.json +44 -0
- maze/levels/courseD_scrat_ramp1.json +46 -0
- maze/levels/courseD_scrat_ramp2.json +47 -0
- maze/levels/courseD_scrat_ramp3.json +45 -0
- maze/levels/courseD_scrat_ramp4.json +46 -0
- maze/levels/courseD_scrat_ramp5.json +46 -0
- maze/levels/courseD_scrat_ramp6.json +47 -0
- maze/levels/courseD_scrat_ramp8.json +47 -0
- maze/levels/courseE_farmer_functions10a1_2023.json +55 -0
- maze/levels/courseE_farmer_functions10a1_2024.json +55 -0
- maze/levels/courseE_farmer_functions11_predict_2024.json +49 -0
- maze/levels/courseE_farmer_functions13_2023.json +55 -0
- maze/levels/courseE_farmer_functions13_2024.json +55 -0
- maze/levels/courseE_farmer_functions1_2023.json +54 -0
- maze/levels/courseE_farmer_functions1_2024.json +54 -0
- maze/levels/courseE_farmer_functions1a_2023.json +55 -0
- maze/levels/courseE_farmer_functions1a_2024.json +55 -0
- maze/levels/courseE_farmer_functions2b_2023.json +55 -0
- maze/levels/courseE_farmer_functions2b_2024.json +55 -0
- maze/levels/courseE_farmer_functions2ba_2023.json +55 -0
- maze/levels/courseE_farmer_functions2ba_2024.json +55 -0
- maze/levels/courseE_farmer_functions3c1_2023.json +56 -0
- maze/levels/courseE_farmer_functions3c1_2024.json +56 -0
- maze/levels/courseE_farmer_functions3c_2023.json +55 -0
- maze/levels/courseE_farmer_functions3c_2024.json +55 -0
- maze/levels/courseE_farmer_functions7a1_2023.json +59 -0
- maze/levels/courseE_farmer_functions7a1_2024.json +59 -0
- maze/levels/courseE_farmer_functions8a1_2023.json +56 -0
- maze/levels/courseE_farmer_functions8a1_2024.json +56 -0
- maze/levels/courseE_farmer_functions9a1_2023.json +58 -0
- maze/levels/courseE_farmer_functions9a1_2024.json +58 -0
- maze/levels/courseE_farmer_ramp11a_2023.json +49 -0
- maze/levels/courseE_farmer_ramp11a_2024.json +49 -0
- maze/levels/courseE_farmer_ramp11b_2023.json +50 -0
- maze/levels/courseE_farmer_ramp11b_2024.json +50 -0
- maze/levels/courseE_farmer_ramp12_forswap_2023.json +50 -0
- maze/levels/courseE_farmer_ramp12_forswap_2024.json +50 -0
- maze/levels/courseE_farmer_ramp12b_2023.json +50 -0
- maze/levels/courseE_farmer_ramp12b_2024.json +50 -0
- maze/levels/courseE_farmer_ramp12c_2023.json +50 -0
- maze/levels/courseE_farmer_ramp12c_2024.json +50 -0
- maze/levels/courseE_farmer_ramp12d_2023.json +50 -0
- maze/levels/courseE_farmer_ramp12d_2024.json +50 -0
- maze/levels/courseE_farmer_ramp12e_2023.json +50 -0
- maze/levels/courseE_farmer_ramp12e_2024.json +50 -0
- maze/levels/courseE_farmer_ramp12f_2023.json +51 -0
- maze/levels/courseE_farmer_ramp12f_2024.json +51 -0
- maze/levels/courseE_farmer_while1_2023.json +54 -0
- maze/levels/courseE_farmer_while1_2024.json +54 -0
- maze/levels/courseF_bee_conditionals10.json +53 -0
- maze/levels/courseF_bee_conditionals1_predict1.json +52 -0
- maze/levels/courseF_bee_conditionals2.json +53 -0
- maze/levels/courseF_bee_conditionals3.json +51 -0
- maze/levels/courseF_bee_conditionals4.json +50 -0
- maze/levels/courseF_bee_conditionals5.json +53 -0
- maze/levels/courseF_bee_conditionals7_predict2.json +53 -0
- maze/levels/courseF_bee_conditionals8.json +54 -0
- maze/levels/courseF_bee_for1.json +50 -0
- maze/levels/courseF_bee_for10.json +53 -0
- maze/levels/courseF_bee_for11.json +48 -0
- maze/levels/courseF_bee_for2.json +51 -0
- maze/levels/courseF_bee_for3_predict1.json +52 -0
- maze/levels/courseF_bee_for4.json +54 -0
- maze/levels/courseF_bee_for5.json +52 -0
- maze/levels/courseF_bee_for6.json +55 -0
- maze/levels/courseF_bee_for7.json +53 -0
- maze/levels/courseF_bee_for8.json +54 -0
- maze/levels/courseF_bee_for9.json +55 -0
- maze/levels/courseF_bee_for_challenge1.json +52 -0
- maze/levels/courseF_bee_for_challenge2.json +48 -0
- maze/levels/courseF_bee_functions2.json +55 -0
- maze/levels/courseF_bee_functions3.json +55 -0
- maze/levels/courseF_bee_functions3a.json +55 -0
- maze/levels/courseF_bee_functions4.json +54 -0
- maze/levels/courseF_bee_functions5a.json +55 -0
- maze/levels/courseF_bee_functions6.json +57 -0
- maze/levels/courseF_bee_functions7.json +55 -0
- maze/levels/courseF_bee_functions8a.json +52 -0
- maze/levels/courseF_bee_functionsPre7.json +56 -0
- maze/levels/courseF_bee_functions_challenge1.json +52 -0
- maze/levels/courseF_bee_functions_challenge2.json +51 -0
- maze/levels/courseF_bee_fwp1.json +52 -0
- maze/levels/courseF_bee_fwp2.json +52 -0
- maze/levels/courseF_bee_fwp3.json +54 -0
- maze/levels/courseF_bee_fwp4.json +52 -0
- maze/levels/courseF_bee_fwp5.json +52 -0
- maze/levels/courseF_bee_fwp6.json +53 -0
- maze/levels/courseF_bee_fwp7.json +52 -0
- maze/levels/courseF_bee_fwp8.json +55 -0
- maze/levels/courseF_bee_fwp9_predict1.json +54 -0
- maze/levels/courseF_bee_fwp_challenge1.json +47 -0
- maze/levels/courseF_bee_fwp_challenge2.json +48 -0
- maze/levels/coursea_maze_ramp1_2024.json +53 -0
- maze/levels/grade2_collector_10_2023.json +45 -0
- maze/levels/grade2_collector_10_2024.json +45 -0
- maze/levels/grade2_collector_A_2023.json +50 -0
- maze/levels/grade2_collector_A_2024.json +50 -0
- maze/levels/grade2_collector_A_predict1_2023.json +46 -0
- maze/levels/grade2_collector_A_predict1_2024.json +46 -0
- maze/maze.py +650 -0
- maze/puzzle.py +75 -0
- maze/pvz.py +62 -0
- maze/screencapture.py +22 -0
- 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
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
|