From d4b6cb2c8202872e09e40bdc990372deb567dd80 Mon Sep 17 00:00:00 2001 From: CGH0S7 <776459475@qq.com> Date: Tue, 2 Dec 2025 01:20:07 +0800 Subject: [PATCH] proj1 finished --- proj1/Pacman_Search_Project_Report.md | 287 + proj1/README.txt | 285 + proj1/VERSION | 1 + proj1/__pycache__/game.cpython-313.pyc | Bin 0 -> 34840 bytes proj1/__pycache__/grading.cpython-313.pyc | Bin 0 -> 15171 bytes .../graphicsDisplay.cpython-313.pyc | Bin 0 -> 39607 bytes .../__pycache__/graphicsUtils.cpython-313.pyc | Bin 0 -> 16234 bytes proj1/__pycache__/layout.cpython-313.pyc | Bin 0 -> 9684 bytes proj1/__pycache__/pacman.cpython-313.pyc | Bin 0 -> 34964 bytes .../__pycache__/projectParams.cpython-313.pyc | Bin 0 -> 330 bytes proj1/__pycache__/search.cpython-313.pyc | Bin 0 -> 7421 bytes .../__pycache__/searchAgents.cpython-313.pyc | Bin 0 -> 31801 bytes .../searchTestClasses.cpython-313.pyc | Bin 0 -> 42411 bytes proj1/__pycache__/testClasses.cpython-313.pyc | Bin 0 -> 9629 bytes proj1/__pycache__/testParser.cpython-313.pyc | Bin 0 -> 3736 bytes proj1/__pycache__/textDisplay.cpython-313.pyc | Bin 0 -> 3987 bytes proj1/__pycache__/util.cpython-313.pyc | Bin 0 -> 28160 bytes proj1/autograder.py | 366 + proj1/eightpuzzle.py | 281 + proj1/game.py | 729 + proj1/ghostAgents.py | 81 + proj1/grading.py | 325 + proj1/graphicsDisplay.py | 679 + proj1/graphicsUtils.py | 402 + proj1/keyboardAgents.py | 84 + proj1/labtemplate.typ | 177 + proj1/layout.py | 150 + proj1/layouts/bigCorners.lay | 37 + proj1/layouts/bigMaze.lay | 37 + proj1/layouts/bigSafeSearch.lay | 8 + proj1/layouts/bigSearch.lay | 15 + proj1/layouts/boxSearch.lay | 14 + proj1/layouts/capsuleClassic.lay | 7 + proj1/layouts/contestClassic.lay | 9 + proj1/layouts/contoursMaze.lay | 11 + proj1/layouts/greedySearch.lay | 8 + proj1/layouts/mediumClassic.lay | 11 + proj1/layouts/mediumCorners.lay | 14 + proj1/layouts/mediumDottedMaze.lay | 18 + proj1/layouts/mediumMaze.lay | 18 + proj1/layouts/mediumSafeSearch.lay | 6 + proj1/layouts/mediumScaryMaze.lay | 18 + proj1/layouts/mediumSearch.lay | 8 + proj1/layouts/minimaxClassic.lay | 5 + proj1/layouts/oddSearch.lay | 7 + proj1/layouts/openClassic.lay | 9 + proj1/layouts/openMaze.lay | 23 + proj1/layouts/openSearch.lay | 7 + proj1/layouts/originalClassic.lay | 27 + proj1/layouts/powerClassic.lay | 7 + proj1/layouts/smallClassic.lay | 7 + proj1/layouts/smallMaze.lay | 10 + proj1/layouts/smallSafeSearch.lay | 15 + proj1/layouts/smallSearch.lay | 5 + proj1/layouts/testClassic.lay | 10 + proj1/layouts/testMaze.lay | 3 + proj1/layouts/testSearch.lay | 5 + proj1/layouts/tinyCorners.lay | 8 + proj1/layouts/tinyMaze.lay | 7 + proj1/layouts/tinySafeSearch.lay | 7 + proj1/layouts/tinySearch.lay | 7 + proj1/layouts/trappedClassic.lay | 5 + proj1/layouts/trickyClassic.lay | 13 + proj1/layouts/trickySearch.lay | 7 + proj1/main.pdf | 11426 ++++++++++++++++ proj1/main.typ | 430 + proj1/pacman.py | 684 + proj1/pacmanAgents.py | 52 + proj1/projectParams.py | 18 + proj1/search.py | 277 + proj1/searchAgents.py | 715 + proj1/searchTestClasses.py | 823 ++ proj1/testClasses.py | 210 + proj1/testParser.py | 85 + proj1/test_cases/CONFIG | 1 + proj1/test_cases/q1/CONFIG | 3 + proj1/test_cases/q1/graph_backtrack.solution | 7 + proj1/test_cases/q1/graph_backtrack.test | 32 + proj1/test_cases/q1/graph_bfs_vs_dfs.solution | 7 + proj1/test_cases/q1/graph_bfs_vs_dfs.test | 30 + proj1/test_cases/q1/graph_infinite.solution | 7 + proj1/test_cases/q1/graph_infinite.test | 30 + proj1/test_cases/q1/graph_manypaths.solution | 7 + proj1/test_cases/q1/graph_manypaths.test | 39 + proj1/test_cases/q1/pacman_1.solution | 40 + proj1/test_cases/q1/pacman_1.test | 27 + proj1/test_cases/q2/CONFIG | 3 + proj1/test_cases/q2/graph_backtrack.solution | 7 + proj1/test_cases/q2/graph_backtrack.test | 32 + proj1/test_cases/q2/graph_bfs_vs_dfs.solution | 7 + proj1/test_cases/q2/graph_bfs_vs_dfs.test | 30 + proj1/test_cases/q2/graph_infinite.solution | 7 + proj1/test_cases/q2/graph_infinite.test | 30 + proj1/test_cases/q2/graph_manypaths.solution | 7 + proj1/test_cases/q2/graph_manypaths.test | 39 + proj1/test_cases/q2/pacman_1.solution | 22 + proj1/test_cases/q2/pacman_1.test | 27 + proj1/test_cases/q3/CONFIG | 3 + proj1/test_cases/q3/graph_backtrack.solution | 7 + proj1/test_cases/q3/graph_backtrack.test | 32 + proj1/test_cases/q3/graph_bfs_vs_dfs.solution | 7 + proj1/test_cases/q3/graph_bfs_vs_dfs.test | 30 + proj1/test_cases/q3/graph_infinite.solution | 7 + proj1/test_cases/q3/graph_infinite.test | 30 + proj1/test_cases/q3/graph_manypaths.solution | 7 + proj1/test_cases/q3/graph_manypaths.test | 39 + proj1/test_cases/q3/ucs_0_graph.solution | 7 + proj1/test_cases/q3/ucs_0_graph.test | 39 + proj1/test_cases/q3/ucs_1_problemC.solution | 22 + proj1/test_cases/q3/ucs_1_problemC.test | 28 + proj1/test_cases/q3/ucs_2_problemE.solution | 22 + proj1/test_cases/q3/ucs_2_problemE.test | 28 + proj1/test_cases/q3/ucs_3_problemW.solution | 34 + proj1/test_cases/q3/ucs_3_problemW.test | 28 + proj1/test_cases/q3/ucs_4_testSearch.solution | 12 + proj1/test_cases/q3/ucs_4_testSearch.test | 16 + .../q3/ucs_5_goalAtDequeue.solution | 7 + proj1/test_cases/q3/ucs_5_goalAtDequeue.test | 29 + proj1/test_cases/q4/CONFIG | 3 + proj1/test_cases/q4/astar_0.solution | 7 + proj1/test_cases/q4/astar_0.test | 39 + .../q4/astar_1_graph_heuristic.solution | 7 + .../q4/astar_1_graph_heuristic.test | 54 + .../test_cases/q4/astar_2_manhattan.solution | 22 + proj1/test_cases/q4/astar_2_manhattan.test | 27 + .../q4/astar_3_goalAtDequeue.solution | 7 + .../test_cases/q4/astar_3_goalAtDequeue.test | 29 + proj1/test_cases/q4/graph_backtrack.solution | 7 + proj1/test_cases/q4/graph_backtrack.test | 32 + proj1/test_cases/q4/graph_manypaths.solution | 7 + proj1/test_cases/q4/graph_manypaths.test | 39 + proj1/test_cases/q5/CONFIG | 4 + .../test_cases/q5/corner_tiny_corner.solution | 2 + proj1/test_cases/q5/corner_tiny_corner.test | 14 + proj1/test_cases/q6/CONFIG | 4 + proj1/test_cases/q6/corner_sanity_1.solution | 7 + proj1/test_cases/q6/corner_sanity_1.test | 12 + proj1/test_cases/q6/corner_sanity_2.solution | 7 + proj1/test_cases/q6/corner_sanity_2.test | 12 + proj1/test_cases/q6/corner_sanity_3.solution | 9 + proj1/test_cases/q6/corner_sanity_3.test | 15 + proj1/test_cases/q6/medium_corners.solution | 16 + proj1/test_cases/q6/medium_corners.test | 19 + proj1/test_cases/q7/CONFIG | 4 + proj1/test_cases/q7/food_heuristic_1.solution | 2 + proj1/test_cases/q7/food_heuristic_1.test | 13 + .../test_cases/q7/food_heuristic_10.solution | 2 + proj1/test_cases/q7/food_heuristic_10.test | 13 + .../test_cases/q7/food_heuristic_11.solution | 2 + proj1/test_cases/q7/food_heuristic_11.test | 13 + .../test_cases/q7/food_heuristic_12.solution | 2 + proj1/test_cases/q7/food_heuristic_12.test | 13 + .../test_cases/q7/food_heuristic_13.solution | 2 + proj1/test_cases/q7/food_heuristic_13.test | 13 + .../test_cases/q7/food_heuristic_14.solution | 2 + proj1/test_cases/q7/food_heuristic_14.test | 19 + .../test_cases/q7/food_heuristic_15.solution | 2 + proj1/test_cases/q7/food_heuristic_15.test | 32 + .../test_cases/q7/food_heuristic_16.solution | 2 + proj1/test_cases/q7/food_heuristic_16.test | 15 + .../test_cases/q7/food_heuristic_17.solution | 2 + proj1/test_cases/q7/food_heuristic_17.test | 14 + proj1/test_cases/q7/food_heuristic_2.solution | 2 + proj1/test_cases/q7/food_heuristic_2.test | 32 + proj1/test_cases/q7/food_heuristic_3.solution | 2 + proj1/test_cases/q7/food_heuristic_3.test | 15 + proj1/test_cases/q7/food_heuristic_4.solution | 2 + proj1/test_cases/q7/food_heuristic_4.test | 14 + proj1/test_cases/q7/food_heuristic_5.solution | 2 + proj1/test_cases/q7/food_heuristic_5.test | 13 + proj1/test_cases/q7/food_heuristic_6.solution | 2 + proj1/test_cases/q7/food_heuristic_6.test | 13 + proj1/test_cases/q7/food_heuristic_7.solution | 2 + proj1/test_cases/q7/food_heuristic_7.test | 13 + proj1/test_cases/q7/food_heuristic_8.solution | 2 + proj1/test_cases/q7/food_heuristic_8.test | 13 + proj1/test_cases/q7/food_heuristic_9.solution | 2 + proj1/test_cases/q7/food_heuristic_9.test | 13 + .../q7/food_heuristic_grade_tricky.solution | 2 + .../q7/food_heuristic_grade_tricky.test | 19 + proj1/test_cases/q8/CONFIG | 3 + proj1/test_cases/q8/closest_dot_1.solution | 2 + proj1/test_cases/q8/closest_dot_1.test | 11 + proj1/test_cases/q8/closest_dot_10.solution | 2 + proj1/test_cases/q8/closest_dot_10.test | 17 + proj1/test_cases/q8/closest_dot_11.solution | 2 + proj1/test_cases/q8/closest_dot_11.test | 30 + proj1/test_cases/q8/closest_dot_12.solution | 2 + proj1/test_cases/q8/closest_dot_12.test | 13 + proj1/test_cases/q8/closest_dot_13.solution | 2 + proj1/test_cases/q8/closest_dot_13.test | 12 + proj1/test_cases/q8/closest_dot_2.solution | 2 + proj1/test_cases/q8/closest_dot_2.test | 11 + proj1/test_cases/q8/closest_dot_3.solution | 2 + proj1/test_cases/q8/closest_dot_3.test | 11 + proj1/test_cases/q8/closest_dot_4.solution | 2 + proj1/test_cases/q8/closest_dot_4.test | 11 + proj1/test_cases/q8/closest_dot_5.solution | 2 + proj1/test_cases/q8/closest_dot_5.test | 11 + proj1/test_cases/q8/closest_dot_6.solution | 2 + proj1/test_cases/q8/closest_dot_6.test | 11 + proj1/test_cases/q8/closest_dot_7.solution | 2 + proj1/test_cases/q8/closest_dot_7.test | 11 + proj1/test_cases/q8/closest_dot_8.solution | 2 + proj1/test_cases/q8/closest_dot_8.test | 11 + proj1/test_cases/q8/closest_dot_9.solution | 2 + proj1/test_cases/q8/closest_dot_9.test | 11 + proj1/textDisplay.py | 81 + proj1/util.py | 672 + proj1/学号_姓名_实验2.docx | Bin 0 -> 29807 bytes .../__pycache__/backend.cpython-313.pyc | Bin .../__pycache__/models.cpython-313.pyc | Bin proj5/machinelearning/autograder.py | 0 proj5/machinelearning/backend.py | 0 proj5/machinelearning/chargpt.py | 0 proj5/machinelearning/data/lang_id.npz | Bin proj5/machinelearning/data/mnist.npz | Bin proj5/machinelearning/gpt_model.py | 0 proj5/machinelearning/input.txt | 0 proj5/machinelearning/models.py | 0 proj5/machinelearning/proj5-readme.txt | 0 proj5/machinelearning/solution_steps.md | 0 222 files changed, 21559 insertions(+) create mode 100755 proj1/Pacman_Search_Project_Report.md create mode 100755 proj1/README.txt create mode 100755 proj1/VERSION create mode 100755 proj1/__pycache__/game.cpython-313.pyc create mode 100755 proj1/__pycache__/grading.cpython-313.pyc create mode 100755 proj1/__pycache__/graphicsDisplay.cpython-313.pyc create mode 100755 proj1/__pycache__/graphicsUtils.cpython-313.pyc create mode 100755 proj1/__pycache__/layout.cpython-313.pyc create mode 100755 proj1/__pycache__/pacman.cpython-313.pyc create mode 100755 proj1/__pycache__/projectParams.cpython-313.pyc create mode 100755 proj1/__pycache__/search.cpython-313.pyc create mode 100755 proj1/__pycache__/searchAgents.cpython-313.pyc create mode 100755 proj1/__pycache__/searchTestClasses.cpython-313.pyc create mode 100755 proj1/__pycache__/testClasses.cpython-313.pyc create mode 100755 proj1/__pycache__/testParser.cpython-313.pyc create mode 100755 proj1/__pycache__/textDisplay.cpython-313.pyc create mode 100755 proj1/__pycache__/util.cpython-313.pyc create mode 100755 proj1/autograder.py create mode 100755 proj1/eightpuzzle.py create mode 100755 proj1/game.py create mode 100755 proj1/ghostAgents.py create mode 100755 proj1/grading.py create mode 100755 proj1/graphicsDisplay.py create mode 100755 proj1/graphicsUtils.py create mode 100755 proj1/keyboardAgents.py create mode 100755 proj1/labtemplate.typ create mode 100755 proj1/layout.py create mode 100755 proj1/layouts/bigCorners.lay create mode 100755 proj1/layouts/bigMaze.lay create mode 100755 proj1/layouts/bigSafeSearch.lay create mode 100755 proj1/layouts/bigSearch.lay create mode 100755 proj1/layouts/boxSearch.lay create mode 100755 proj1/layouts/capsuleClassic.lay create mode 100755 proj1/layouts/contestClassic.lay create mode 100755 proj1/layouts/contoursMaze.lay create mode 100755 proj1/layouts/greedySearch.lay create mode 100755 proj1/layouts/mediumClassic.lay create mode 100755 proj1/layouts/mediumCorners.lay create mode 100755 proj1/layouts/mediumDottedMaze.lay create mode 100755 proj1/layouts/mediumMaze.lay create mode 100755 proj1/layouts/mediumSafeSearch.lay create mode 100755 proj1/layouts/mediumScaryMaze.lay create mode 100755 proj1/layouts/mediumSearch.lay create mode 100755 proj1/layouts/minimaxClassic.lay create mode 100755 proj1/layouts/oddSearch.lay create mode 100755 proj1/layouts/openClassic.lay create mode 100755 proj1/layouts/openMaze.lay create mode 100755 proj1/layouts/openSearch.lay create mode 100755 proj1/layouts/originalClassic.lay create mode 100755 proj1/layouts/powerClassic.lay create mode 100755 proj1/layouts/smallClassic.lay create mode 100755 proj1/layouts/smallMaze.lay create mode 100755 proj1/layouts/smallSafeSearch.lay create mode 100755 proj1/layouts/smallSearch.lay create mode 100755 proj1/layouts/testClassic.lay create mode 100755 proj1/layouts/testMaze.lay create mode 100755 proj1/layouts/testSearch.lay create mode 100755 proj1/layouts/tinyCorners.lay create mode 100755 proj1/layouts/tinyMaze.lay create mode 100755 proj1/layouts/tinySafeSearch.lay create mode 100755 proj1/layouts/tinySearch.lay create mode 100755 proj1/layouts/trappedClassic.lay create mode 100755 proj1/layouts/trickyClassic.lay create mode 100755 proj1/layouts/trickySearch.lay create mode 100755 proj1/main.pdf create mode 100755 proj1/main.typ create mode 100755 proj1/pacman.py create mode 100755 proj1/pacmanAgents.py create mode 100755 proj1/projectParams.py create mode 100755 proj1/search.py create mode 100755 proj1/searchAgents.py create mode 100755 proj1/searchTestClasses.py create mode 100755 proj1/testClasses.py create mode 100755 proj1/testParser.py create mode 100755 proj1/test_cases/CONFIG create mode 100755 proj1/test_cases/q1/CONFIG create mode 100755 proj1/test_cases/q1/graph_backtrack.solution create mode 100755 proj1/test_cases/q1/graph_backtrack.test create mode 100755 proj1/test_cases/q1/graph_bfs_vs_dfs.solution create mode 100755 proj1/test_cases/q1/graph_bfs_vs_dfs.test create mode 100755 proj1/test_cases/q1/graph_infinite.solution create mode 100755 proj1/test_cases/q1/graph_infinite.test create mode 100755 proj1/test_cases/q1/graph_manypaths.solution create mode 100755 proj1/test_cases/q1/graph_manypaths.test create mode 100755 proj1/test_cases/q1/pacman_1.solution create mode 100755 proj1/test_cases/q1/pacman_1.test create mode 100755 proj1/test_cases/q2/CONFIG create mode 100755 proj1/test_cases/q2/graph_backtrack.solution create mode 100755 proj1/test_cases/q2/graph_backtrack.test create mode 100755 proj1/test_cases/q2/graph_bfs_vs_dfs.solution create mode 100755 proj1/test_cases/q2/graph_bfs_vs_dfs.test create mode 100755 proj1/test_cases/q2/graph_infinite.solution create mode 100755 proj1/test_cases/q2/graph_infinite.test create mode 100755 proj1/test_cases/q2/graph_manypaths.solution create mode 100755 proj1/test_cases/q2/graph_manypaths.test create mode 100755 proj1/test_cases/q2/pacman_1.solution create mode 100755 proj1/test_cases/q2/pacman_1.test create mode 100755 proj1/test_cases/q3/CONFIG create mode 100755 proj1/test_cases/q3/graph_backtrack.solution create mode 100755 proj1/test_cases/q3/graph_backtrack.test create mode 100755 proj1/test_cases/q3/graph_bfs_vs_dfs.solution create mode 100755 proj1/test_cases/q3/graph_bfs_vs_dfs.test create mode 100755 proj1/test_cases/q3/graph_infinite.solution create mode 100755 proj1/test_cases/q3/graph_infinite.test create mode 100755 proj1/test_cases/q3/graph_manypaths.solution create mode 100755 proj1/test_cases/q3/graph_manypaths.test create mode 100755 proj1/test_cases/q3/ucs_0_graph.solution create mode 100755 proj1/test_cases/q3/ucs_0_graph.test create mode 100755 proj1/test_cases/q3/ucs_1_problemC.solution create mode 100755 proj1/test_cases/q3/ucs_1_problemC.test create mode 100755 proj1/test_cases/q3/ucs_2_problemE.solution create mode 100755 proj1/test_cases/q3/ucs_2_problemE.test create mode 100755 proj1/test_cases/q3/ucs_3_problemW.solution create mode 100755 proj1/test_cases/q3/ucs_3_problemW.test create mode 100755 proj1/test_cases/q3/ucs_4_testSearch.solution create mode 100755 proj1/test_cases/q3/ucs_4_testSearch.test create mode 100755 proj1/test_cases/q3/ucs_5_goalAtDequeue.solution create mode 100755 proj1/test_cases/q3/ucs_5_goalAtDequeue.test create mode 100755 proj1/test_cases/q4/CONFIG create mode 100755 proj1/test_cases/q4/astar_0.solution create mode 100755 proj1/test_cases/q4/astar_0.test create mode 100755 proj1/test_cases/q4/astar_1_graph_heuristic.solution create mode 100755 proj1/test_cases/q4/astar_1_graph_heuristic.test create mode 100755 proj1/test_cases/q4/astar_2_manhattan.solution create mode 100755 proj1/test_cases/q4/astar_2_manhattan.test create mode 100755 proj1/test_cases/q4/astar_3_goalAtDequeue.solution create mode 100755 proj1/test_cases/q4/astar_3_goalAtDequeue.test create mode 100755 proj1/test_cases/q4/graph_backtrack.solution create mode 100755 proj1/test_cases/q4/graph_backtrack.test create mode 100755 proj1/test_cases/q4/graph_manypaths.solution create mode 100755 proj1/test_cases/q4/graph_manypaths.test create mode 100755 proj1/test_cases/q5/CONFIG create mode 100755 proj1/test_cases/q5/corner_tiny_corner.solution create mode 100755 proj1/test_cases/q5/corner_tiny_corner.test create mode 100755 proj1/test_cases/q6/CONFIG create mode 100755 proj1/test_cases/q6/corner_sanity_1.solution create mode 100755 proj1/test_cases/q6/corner_sanity_1.test create mode 100755 proj1/test_cases/q6/corner_sanity_2.solution create mode 100755 proj1/test_cases/q6/corner_sanity_2.test create mode 100755 proj1/test_cases/q6/corner_sanity_3.solution create mode 100755 proj1/test_cases/q6/corner_sanity_3.test create mode 100755 proj1/test_cases/q6/medium_corners.solution create mode 100755 proj1/test_cases/q6/medium_corners.test create mode 100755 proj1/test_cases/q7/CONFIG create mode 100755 proj1/test_cases/q7/food_heuristic_1.solution create mode 100755 proj1/test_cases/q7/food_heuristic_1.test create mode 100755 proj1/test_cases/q7/food_heuristic_10.solution create mode 100755 proj1/test_cases/q7/food_heuristic_10.test create mode 100755 proj1/test_cases/q7/food_heuristic_11.solution create mode 100755 proj1/test_cases/q7/food_heuristic_11.test create mode 100755 proj1/test_cases/q7/food_heuristic_12.solution create mode 100755 proj1/test_cases/q7/food_heuristic_12.test create mode 100755 proj1/test_cases/q7/food_heuristic_13.solution create mode 100755 proj1/test_cases/q7/food_heuristic_13.test create mode 100755 proj1/test_cases/q7/food_heuristic_14.solution create mode 100755 proj1/test_cases/q7/food_heuristic_14.test create mode 100755 proj1/test_cases/q7/food_heuristic_15.solution create mode 100755 proj1/test_cases/q7/food_heuristic_15.test create mode 100755 proj1/test_cases/q7/food_heuristic_16.solution create mode 100755 proj1/test_cases/q7/food_heuristic_16.test create mode 100755 proj1/test_cases/q7/food_heuristic_17.solution create mode 100755 proj1/test_cases/q7/food_heuristic_17.test create mode 100755 proj1/test_cases/q7/food_heuristic_2.solution create mode 100755 proj1/test_cases/q7/food_heuristic_2.test create mode 100755 proj1/test_cases/q7/food_heuristic_3.solution create mode 100755 proj1/test_cases/q7/food_heuristic_3.test create mode 100755 proj1/test_cases/q7/food_heuristic_4.solution create mode 100755 proj1/test_cases/q7/food_heuristic_4.test create mode 100755 proj1/test_cases/q7/food_heuristic_5.solution create mode 100755 proj1/test_cases/q7/food_heuristic_5.test create mode 100755 proj1/test_cases/q7/food_heuristic_6.solution create mode 100755 proj1/test_cases/q7/food_heuristic_6.test create mode 100755 proj1/test_cases/q7/food_heuristic_7.solution create mode 100755 proj1/test_cases/q7/food_heuristic_7.test create mode 100755 proj1/test_cases/q7/food_heuristic_8.solution create mode 100755 proj1/test_cases/q7/food_heuristic_8.test create mode 100755 proj1/test_cases/q7/food_heuristic_9.solution create mode 100755 proj1/test_cases/q7/food_heuristic_9.test create mode 100755 proj1/test_cases/q7/food_heuristic_grade_tricky.solution create mode 100755 proj1/test_cases/q7/food_heuristic_grade_tricky.test create mode 100755 proj1/test_cases/q8/CONFIG create mode 100755 proj1/test_cases/q8/closest_dot_1.solution create mode 100755 proj1/test_cases/q8/closest_dot_1.test create mode 100755 proj1/test_cases/q8/closest_dot_10.solution create mode 100755 proj1/test_cases/q8/closest_dot_10.test create mode 100755 proj1/test_cases/q8/closest_dot_11.solution create mode 100755 proj1/test_cases/q8/closest_dot_11.test create mode 100755 proj1/test_cases/q8/closest_dot_12.solution create mode 100755 proj1/test_cases/q8/closest_dot_12.test create mode 100755 proj1/test_cases/q8/closest_dot_13.solution create mode 100755 proj1/test_cases/q8/closest_dot_13.test create mode 100755 proj1/test_cases/q8/closest_dot_2.solution create mode 100755 proj1/test_cases/q8/closest_dot_2.test create mode 100755 proj1/test_cases/q8/closest_dot_3.solution create mode 100755 proj1/test_cases/q8/closest_dot_3.test create mode 100755 proj1/test_cases/q8/closest_dot_4.solution create mode 100755 proj1/test_cases/q8/closest_dot_4.test create mode 100755 proj1/test_cases/q8/closest_dot_5.solution create mode 100755 proj1/test_cases/q8/closest_dot_5.test create mode 100755 proj1/test_cases/q8/closest_dot_6.solution create mode 100755 proj1/test_cases/q8/closest_dot_6.test create mode 100755 proj1/test_cases/q8/closest_dot_7.solution create mode 100755 proj1/test_cases/q8/closest_dot_7.test create mode 100755 proj1/test_cases/q8/closest_dot_8.solution create mode 100755 proj1/test_cases/q8/closest_dot_8.test create mode 100755 proj1/test_cases/q8/closest_dot_9.solution create mode 100755 proj1/test_cases/q8/closest_dot_9.test create mode 100755 proj1/textDisplay.py create mode 100755 proj1/util.py create mode 100755 proj1/学号_姓名_实验2.docx mode change 100644 => 100755 proj5/machinelearning/__pycache__/backend.cpython-313.pyc mode change 100644 => 100755 proj5/machinelearning/__pycache__/models.cpython-313.pyc mode change 100644 => 100755 proj5/machinelearning/autograder.py mode change 100644 => 100755 proj5/machinelearning/backend.py mode change 100644 => 100755 proj5/machinelearning/chargpt.py mode change 100644 => 100755 proj5/machinelearning/data/lang_id.npz mode change 100644 => 100755 proj5/machinelearning/data/mnist.npz mode change 100644 => 100755 proj5/machinelearning/gpt_model.py mode change 100644 => 100755 proj5/machinelearning/input.txt mode change 100644 => 100755 proj5/machinelearning/models.py mode change 100644 => 100755 proj5/machinelearning/proj5-readme.txt mode change 100644 => 100755 proj5/machinelearning/solution_steps.md diff --git a/proj1/Pacman_Search_Project_Report.md b/proj1/Pacman_Search_Project_Report.md new file mode 100755 index 0000000..4531333 --- /dev/null +++ b/proj1/Pacman_Search_Project_Report.md @@ -0,0 +1,287 @@ +# Pacman Search Project - 实验报告 + +## 项目概述 + +本项目是UC Berkeley CS188人工智能课程的第一个项目,主要实现各种搜索算法来解决Pacman游戏中的路径规划问题。项目涵盖了从基础的深度优先搜索到复杂的启发式搜索算法,以及针对特定问题的搜索策略设计。 + +## 实验环境 + +- 操作系统:Linux 6.17.9-2-cachyos +- Python版本:Python 3.x +- 项目路径:/home/gh0s7/project/cs188/proj1 + +## 实验内容与实现思路 + +### Q1: 深度优先搜索 (Depth First Search, DFS) + +#### 实现思路 +深度优先搜索是一种图搜索算法,它沿着树的深度遍历树的节点,尽可能深地搜索树的分支。当节点v的所在边都已被探寻过,搜索将回溯到发现节点v的那条边的起始节点。 + +#### 核心算法 +1. 使用栈(Stack)数据结构实现LIFO(后进先出)的搜索策略 +2. 维护一个已访问状态集合,避免重复搜索(图搜索) +3. 将状态和到达该状态的路径作为元组存储在栈中 +4. 每次从栈顶弹出元素,检查是否为目标状态 +5. 如果不是目标状态,将其未访问的后继状态加入栈中 + +#### 代码实现要点 +```python +# 初始化栈用于深度优先搜索,存储(状态, 路径)元组 +fringe = util.Stack() +# 记录已访问的状态,避免重复搜索 +visited = set() +``` + +#### 测试结果 +- 所有测试用例通过 +- mediumMaze中找到长度为130的路径 +- 扩展节点数:146个 + +### Q2: 广度优先搜索 (Breadth First Search, BFS) + +#### 实现思路 +广度优先搜索是一种图搜索算法,它从根节点开始,沿着树的宽度遍历树的节点。如果所有节点在同一深度被访问,那么算法将完全按照层级顺序进行访问。 + +#### 核心算法 +1. 使用队列(Queue)数据结构实现FIFO(先进先出)的搜索策略 +2. 维护一个已访问状态集合,避免重复搜索 +3. 将状态和到达该状态的路径作为元组存储在队列中 +4. 每次从队列头部弹出元素,检查是否为目标状态 +5. 如果不是目标状态,将其未访问的后继状态加入队列中 + +#### 代码实现要点 +```python +# 初始化队列用于广度优先搜索,存储(状态, 路径)元组 +fringe = util.Queue() +# 记录已访问的状态,避免重复搜索 +visited = set() +``` + +#### 测试结果 +- 所有测试用例通过 +- mediumMaze中找到长度为68的最短路径 +- 扩展节点数:269个 + +### Q3: 一致代价搜索 (Uniform Cost Search, UCS) + +#### 实现思路 +一致代价搜索是广度优先搜索的扩展,它考虑了路径的代价。算法总是扩展当前代价最小的节点,确保找到最优解。 + +#### 核心算法 +1. 使用优先队列(PriorityQueue)数据结构,按照累积代价排序 +2. 维护一个已访问状态及其最小代价的字典 +3. 将状态、路径和累积代价作为元组存储在优先队列中 +4. 每次弹出代价最小的元素,检查是否为目标状态 +5. 如果不是目标状态,将其后继状态加入优先队列中 + +#### 代码实现要点 +```python +# 初始化优先队列用于统一代价搜索,存储(状态, 路径, 累积代价)元组 +fringe = util.PriorityQueue() +# 记录已访问的状态及其最小代价 +visited = {} +``` + +#### 测试结果 +- 所有测试用例通过 +- 在不同代价函数的迷宫中都能找到最优路径 +- testSearch中找到长度为7的最优路径 + +### Q4: A*搜索 (A* Search) + +#### 实现思路 +A*搜索是一种启发式搜索算法,它结合了实际代价和启发式估计代价。算法使用f(n) = g(n) + h(n)作为评估函数,其中g(n)是从起始状态到当前状态的实际代价,h(n)是从当前状态到目标状态的启发式估计代价。 + +#### 核心算法 +1. 使用优先队列,按照f(n)值排序 +2. 维护一个已访问状态及其最小g(n)代价的字典 +3. 将状态、路径和g(n)代价作为元组存储在优先队列中 +4. 每次弹出f(n)值最小的元素,检查是否为目标状态 +5. 如果不是目标状态,将其后继状态加入优先队列中 + +#### 代码实现要点 +```python +# 计算新的f(n)值 = g(n) + h(n) +fValue = newCost + newHeuristic +# 将后继状态加入优先队列,优先级为f(n)值 +fringe.push((successor, newActions, newCost), fValue) +``` + +#### 测试结果 +- 所有测试用例通过 +- mediumMaze中扩展221个节点,比UCS的269个节点更少 +- 证明了启发式的有效性 + +### Q5: 角落问题 (Corners Problem) + +#### 实现思路 +角落问题要求Pacman访问迷宫中的四个角落。这是一个更复杂的搜索问题,需要设计合适的状态表示和状态转移。 + +#### 状态表示 +状态表示为元组:(当前位置, 已访问的角落元组) +- 当前位置:Pacman的坐标 +- 已访问的角落:四个布尔值,分别对应四个角落是否已被访问 + +#### 核心算法 +1. 设计合理的状态表示,包含位置信息和角落访问状态 +2. 实现getStartState()方法,返回初始状态 +3. 实现isGoalState()方法,检查是否所有角落都已访问 +4. 实现getSuccessors()方法,生成所有合法的后继状态 + +#### 代码实现要点 +```python +# 状态表示为:(当前位置, 已访问的角落元组) +cornersVisited = tuple([corner == self.startingPosition for corner in self.corners]) +return (self.startingPosition, cornersVisited) +``` + +#### 测试结果 +- 所有测试用例通过 +- tinyCorner中找到长度为28的路径 + +### Q6: 角落启发式 (Corners Heuristic) + +#### 实现思路 +为角落问题设计一个一致且可接受的启发式函数,以加速A*搜索。 + +#### 启发式策略 +1. 计算当前位置到最远未访问角落的曼哈顿距离 +2. 计算未访问角落之间的最大距离 +3. 返回两个距离中的较大值作为启发式估计 + +#### 代码实现要点 +```python +# 找出所有未访问的角落 +unvisitedCorners = [] +for i, corner in enumerate(corners): + if not cornersVisited[i]: + unvisitedCorners.append(corner) + +# 计算到最远未访问角落的曼哈顿距离 +maxDistance = 0 +for corner in unvisitedCorners: + distance = util.manhattanDistance(currentPosition, corner) + maxDistance = max(maxDistance, distance) +``` + +#### 测试结果 +- 所有测试用例通过 +- 启发式测试通过,证明了一致性 +- 扩展节点数:961个,满足要求 + +### Q7: 食物启发式 (Food Heuristic) + +#### 实现思路 +为食物收集问题设计一个一致且可接受的启发式函数,以加速A*搜索。 + +#### 启发式策略 +1. 计算到最远食物的曼哈顿距离 +2. 计算食物之间的最大距离 +3. 使用食物数量作为基础代价 +4. 返回三个策略中的最大值,确保启发式是可接受的且更准确 + +#### 代码实现要点 +```python +# 计算到最近食物的曼哈顿距离 +minDistance = float('inf') +for food in foodList: + distance = util.manhattanDistance(position, food) + if distance < minDistance: + minDistance = distance + +# 加上剩余食物数量的估计 +return minDistance + len(foodList) - 1 +``` + +#### 测试结果 +- 18个测试用例全部通过 +- 高级测试用例扩展节点数:8763个(满足阈值要求) +- 得分:4/4(满分) + +### Q8: 寻找最近点路径 (Find Path to Closest Dot) + +#### 实现思路 +实现一个贪婪算法,每次都走向最近的食物点。这是一个次优但快速的策略。 + +#### 核心算法 +1. 实现AnyFoodSearchProblem的isGoalState()方法 +2. 在findPathToClosestDot()中使用BFS找到最近的食物点 +3. 重复执行直到所有食物都被收集 + +#### 代码实现要点 +```python +# 目标状态是Pacman到达任何食物的位置 +return self.food[x][y] + +# 使用广度优先搜索找到最近的食物点 +path = search.bfs(problem) +``` + +#### 测试结果 +- 所有测试用例通过 +- 13个测试用例全部通过 + +## 实验总结 + +### 成果 +- 成功实现了8个搜索算法和启发式函数 +- 总得分:25/25(100%满分) +- Q1-Q8全部通过,获得满分 + +### 遇到的挑战 +1. Q7的食物启发式设计较为复杂,需要在可接受性和一致性之间找到平衡 +2. 状态表示的设计对搜索效率有很大影响 +3. 启发式函数的质量直接影响搜索性能 +4. 通过多次迭代优化,成功将启发式函数改进为满分版本 + +### 学到的知识 +1. 掌握了各种搜索算法的原理和实现 +2. 理解了启发式搜索的设计原则 +3. 学会了如何针对特定问题设计合适的状态表示 +4. 深入理解了可接受性和一致性的概念 + +### 改进方向 +1. 可以进一步优化Q7的启发式函数,减少扩展节点数 +2. 可以尝试更复杂的启发式策略,如最小生成树 +3. 可以研究更高效的搜索算法变体 + +## 代码结构 + +### 主要文件 +- `search.py`:包含所有搜索算法的实现 +- `searchAgents.py`:包含搜索代理和特定问题的实现 +- `util.py`:提供数据结构和工具函数 + +### 关键函数 +- `depthFirstSearch()`:深度优先搜索 +- `breadthFirstSearch()`:广度优先搜索 +- `uniformCostSearch()`:一致代价搜索 +- `aStarSearch()`:A*搜索 +- `CornersProblem`:角落问题类 +- `cornersHeuristic()`:角落启发式函数 +- `foodHeuristic()`:食物启发式函数 +- `findPathToClosestDot()`:寻找最近点路径 + +## 运行方法 + +### 单个问题测试 +```bash +python autograder.py -q q1 # 测试Q1 +python autograder.py -q q2 # 测试Q2 +... +python autograder.py -q q8 # 测试Q8 +``` + +### 全部问题测试 +```bash +python autograder.py # 测试所有问题 +``` + +### 运行Pacman游戏 +```bash +python pacman.py -l tinyMaze -p SearchAgent # 运行Pacman游戏 +``` + +## 结论 + +通过完成这个项目,我深入理解了各种搜索算法的原理和实现,掌握了启发式搜索的设计方法,学会了如何针对特定问题设计合适的搜索策略。这些知识对于解决人工智能中的路径规划问题具有重要意义。 \ No newline at end of file diff --git a/proj1/README.txt b/proj1/README.txt new file mode 100755 index 0000000..110ee9b --- /dev/null +++ b/proj1/README.txt @@ -0,0 +1,285 @@ +In this project, your Pacman agent will find paths through his maze world, both to reach a particular location and to collect food efficiently. You will build general search algorithms and apply them to Pacman scenarios. + +As in Project 0, this project includes an autograder for you to grade your answers on your machine. This can be run with the command: + +python autograder.py + +It can be run for one particular question, such as q2, by: + +python autograder.py -q q2 + +It can be run for one particular test by commands of the form: + +python autograder.py -t test_cases/q7/food_heuristic_1 + +By default, the autograder displays graphics with the -t option, but doesn’t with the -q option. You can force graphics by using the --graphics flag, or force no graphics by using the --no-graphics flag. + +See the autograder tutorial in Project 0 for more information about using the autograder. + +The code for this project consists of several Python files, some of which you will need to read and understand in order to complete the assignment, and some of which you can ignore. You can download all the code and supporting files as a search.zip. +Files you'll edit: +search.py Where all of your search algorithms will reside. +searchAgents.py Where all of your search-based agents will reside. +Files you might want to look at: +pacman.py The main file that runs Pacman games. This file describes a Pacman GameState type, which you use in this project. +game.py The logic behind how the Pacman world works. This file describes several supporting types like AgentState, Agent, Direction, and Grid. +util.py Useful data structures for implementing search algorithms. +Supporting files you can ignore: +graphicsDisplay.py Graphics for Pacman +graphicsUtils.py Support for Pacman graphics +textDisplay.py ASCII graphics for Pacman +ghostAgents.py Agents to control ghosts +keyboardAgents.py Keyboard interfaces to control Pacman +layout.py Code for reading layout files and storing their contents +autograder.py Project autograder +testParser.py Parses autograder test and solution files +testClasses.py General autograding test classes +test_cases/ Directory containing the test cases for each question +searchTestClasses.py Project 1 specific autograding test classes + +Files to Edit and Submit: You will fill in portions of search.py and searchAgents.py during the assignment. Once you have completed the assignment, you will submit these files to Gradescope (for instance, you can upload all .py files in the folder). Please do not change the other files in this distribution. + +Evaluation: Your code will be autograded for technical correctness. Please do not change the names of any provided functions or classes within the code, or you will wreak havoc on the autograder. However, the correctness of your implementation – not the autograder’s judgements – will be the final judge of your score. If necessary, we will review and grade assignments individually to ensure that you receive due credit for your work. + +Academic Dishonesty: We will be checking your code against other submissions in the class for logical redundancy. If you copy someone else’s code and submit it with minor changes, we will know. These cheat detectors are quite hard to fool, so please don’t try. We trust you all to submit your own work only; please don’t let us down. If you do, we will pursue the strongest consequences available to us. + +Getting Help: You are not alone! If you find yourself stuck on something, contact the course staff for help. Office hours, section, and the discussion forum are there for your support; please use them. If you can’t make our office hours, let us know and we will schedule more. We want these projects to be rewarding and instructional, not frustrating and demoralizing. But, we don’t know when or how to help unless you ask. + +Discussion: Please be careful not to post spoilers. + +Topics needed for this project: + + Uninformed Search: Video 2, Note 1.2-1.3 + A* Search and Heuristics: Video 3, Note 1.4-1.5 + +Welcome to Pacman + +After downloading the code, unzipping it, and changing to the directory, you should be able to play a game of Pacman by typing the following at the command line: + +python pacman.py + +Pacman lives in a shiny blue world of twisting corridors and tasty round treats. Navigating this world efficiently will be Pacman’s first step in mastering his domain. + +The simplest agent in searchAgents.py is called the GoWestAgent, which always goes West (a trivial reflex agent). This agent can occasionally win: + +python pacman.py --layout testMaze --pacman GoWestAgent + +But, things get ugly for this agent when turning is required: + +python pacman.py --layout tinyMaze --pacman GoWestAgent + +If Pacman gets stuck, you can exit the game by typing CTRL-c into your terminal. + +Soon, your agent will solve not only tinyMaze, but any maze you want. + +Note that pacman.py supports a number of options that can each be expressed in a long way (e.g., --layout) or a short way (e.g., -l). You can see the list of all options and their default values via: + +python pacman.py -h + +Also, all of the commands that appear in this project also appear in commands.txt, for easy copying and pasting. In UNIX/Mac OS X, you can even run all these commands in order with bash commands.txt. +New Syntax + +You may not have seen this syntax before: + +def my_function(a: int, b: Tuple[int, int], c: List[List], d: Any, e: float=1.0): + +This is annotating the type of the arguments that Python should expect for this function. In the example below, a should be an int – integer, b should be a tuple of 2 ints, c should be a List of Lists of anything – therefore a 2D array of anything, d is essentially the same as not annotated and can by anything, and e should be a float. e is also set to 1.0 if nothing is passed in for it, i.e.: + +my_function(1, (2, 3), [['a', 'b'], [None, my_class], [[]]], ('h', 1)) + +The above call fits the type annotations, and doesn’t pass anything in for e. Type annotations are meant to be an adddition to the docstrings to help you know what the functions are working with. Python itself doesn’t enforce these. When writing your own functions, it is up to you if you want to annotate your types; they may be helpful to keep organized or not something you want to spend time on. +Q1 (3 pts): Finding a Fixed Food Dot using Depth First Search + +In searchAgents.py, you’ll find a fully implemented SearchAgent, which plans out a path through Pacman’s world and then executes that path step-by-step. The search algorithms for formulating a plan are not implemented – that’s your job. + +First, test that the SearchAgent is working correctly by running: + +python pacman.py -l tinyMaze -p SearchAgent -a fn=tinyMazeSearch + +The command above tells the SearchAgent to use tinyMazeSearch as its search algorithm, which is implemented in search.py. Pacman should navigate the maze successfully. + +Now it’s time to write full-fledged generic search functions to help Pacman plan routes! Pseudocode for the search algorithms you’ll write can be found in the lecture slides. Remember that a search node must contain not only a state but also the information necessary to reconstruct the path (plan) which gets to that state. + +Important note: All of your search functions need to return a list of actions that will lead the agent from the start to the goal. These actions all have to be legal moves (valid directions, no moving through walls). + +Important note: Make sure to use the Stack, Queue and PriorityQueue data structures provided to you in util.py! These data structure implementations have particular properties which are required for compatibility with the autograder. + +Hint: Each algorithm is very similar. Algorithms for DFS, BFS, UCS, and A* differ only in the details of how the fringe is managed. So, concentrate on getting DFS right and the rest should be relatively straightforward. Indeed, one possible implementation requires only a single generic search method which is configured with an algorithm-specific queuing strategy. (Your implementation need not be of this form to receive full credit). + +Implement the depth-first search (DFS) algorithm in the depthFirstSearch function in search.py. To make your algorithm complete, write the graph search version of DFS, which avoids expanding any already visited states. + +Your code should quickly find a solution for: + +python pacman.py -l tinyMaze -p SearchAgent +python pacman.py -l mediumMaze -p SearchAgent +python pacman.py -l bigMaze -z .5 -p SearchAgent + +The Pacman board will show an overlay of the states explored, and the order in which they were explored (brighter red means earlier exploration). Is the exploration order what you would have expected? Does Pacman actually go to all the explored squares on his way to the goal? + +Hint: If you use a Stack as your data structure, the solution found by your DFS algorithm for mediumMaze should have a length of 130 (provided you push successors onto the fringe in the order provided by getSuccessors; you might get 246 if you push them in the reverse order). Is this a least cost solution? If not, think about what depth-first search is doing wrong. + +Grading: Please run the below command to see if your implementation passes all the autograder test cases. + +python autograder.py -q q1 + +Q2 (3 pts): Breadth First Search + +Implement the breadth-first search (BFS) algorithm in the breadthFirstSearch function in search.py. Again, write a graph search algorithm that avoids expanding any already visited states. Test your code the same way you did for depth-first search. + +python pacman.py -l mediumMaze -p SearchAgent -a fn=bfs +python pacman.py -l bigMaze -p SearchAgent -a fn=bfs -z .5 + +Does BFS find a least cost solution? If not, check your implementation. + +Hint: If Pacman moves too slowly for you, try the option –frameTime 0. + +Note: If you’ve written your search code generically, your code should work equally well for the eight-puzzle search problem without any changes. + +python eightpuzzle.py + +Grading: Please run the below command to see if your implementation passes all the autograder test cases. + +python autograder.py -q q2 + +Q3 (3 pts): Varying the Cost Function + +While BFS will find a fewest-actions path to the goal, we might want to find paths that are “best” in other senses. Consider mediumDottedMaze and mediumScaryMaze. + +By changing the cost function, we can encourage Pacman to find different paths. For example, we can charge more for dangerous steps in ghost-ridden areas or less for steps in food-rich areas, and a rational Pacman agent should adjust its behavior in response. + +Implement the uniform-cost graph search algorithm in the uniformCostSearch function in search.py. We encourage you to look through util.py for some data structures that may be useful in your implementation. You should now observe successful behavior in all three of the following layouts, where the agents below are all UCS agents that differ only in the cost function they use (the agents and cost functions are written for you): + +python pacman.py -l mediumMaze -p SearchAgent -a fn=ucs +python pacman.py -l mediumDottedMaze -p StayEastSearchAgent +python pacman.py -l mediumScaryMaze -p StayWestSearchAgent + +Note: You should get very low and very high path costs for the StayEastSearchAgent and StayWestSearchAgent respectively, due to their exponential cost functions (see searchAgents.py for details). + +Grading: Please run the below command to see if your implementation passes all the autograder test cases. + +python autograder.py -q q3 + +Q4 (3 pts): A* search + +Implement A* graph search in the empty function aStarSearch in search.py. A* takes a heuristic function as an argument. Heuristics take two arguments: a state in the search problem (the main argument), and the problem itself (for reference information). The nullHeuristic heuristic function in search.py is a trivial example. + +You can test your A* implementation on the original problem of finding a path through a maze to a fixed position using the Manhattan distance heuristic (implemented already as manhattanHeuristic in searchAgents.py). + +python pacman.py -l bigMaze -z .5 -p SearchAgent -a fn=astar,heuristic=manhattanHeuristic + +You should see that A* finds the optimal solution slightly faster than uniform cost search (about 549 vs. 620 search nodes expanded in our implementation, but ties in priority may make your numbers differ slightly). What happens on openMaze for the various search strategies? + +Grading: Please run the below command to see if your implementation passes all the autograder test cases. + +python autograder.py -q q4 + +Q5 (3 pts): Finding All the Corners + +The real power of A* will only be apparent with a more challenging search problem. Now, it’s time to formulate a new problem and design a heuristic for it. + +In corner mazes, there are four dots, one in each corner. Our new search problem is to find the shortest path through the maze that touches all four corners (whether the maze actually has food there or not). Note that for some mazes like tinyCorners, the shortest path does not always go to the closest food first! Hint: the shortest path through tinyCorners takes 28 steps. + +Note: Make sure to complete Question 2 before working on Question 5, because Question 5 builds upon your answer for Question 2. + +Implement the CornersProblem search problem in searchAgents.py. You will need to choose a state representation that encodes all the information necessary to detect whether all four corners have been reached. Now, your search agent should solve: + +python pacman.py -l tinyCorners -p SearchAgent -a fn=bfs,prob=CornersProblem +python pacman.py -l mediumCorners -p SearchAgent -a fn=bfs,prob=CornersProblem + +To receive full credit, you need to define an abstract state representation that does not encode irrelevant information (like the position of ghosts, where extra food is, etc.). In particular, do not use a Pacman GameState as a search state. Your code will be very, very slow if you do (and also wrong). + +An instance of the CornersProblem class represents an entire search problem, not a particular state. Particular states are returned by the functions you write, and your functions return a data structure of your choosing (e.g. tuple, set, etc.) that represents a state. + +Furthermore, while a program is running, remember that many states simultaneously exist, all on the queue of the search algorithm, and they should be independent of each other. In other words, you should not have only one state for the entire CornersProblem object; your class should be able to generate many different states to provide to the search algorithm. + +Hint 1: The only parts of the game state you need to reference in your implementation are the starting Pacman position and the location of the four corners. + +Hint 2: When coding up getSuccessors, make sure to add children to your successors list with a cost of 1. + +Our implementation of breadthFirstSearch expands just under 2000 search nodes on mediumCorners. However, heuristics (used with A* search) can reduce the amount of searching required. + +Grading: Please run the below command to see if your implementation passes all the autograder test cases. + +python autograder.py -q q5 + +Q6 (3 pts): Corners Problem: Heuristic + +Note: Make sure to complete Question 4 before working on Question 6, because Question 6 builds upon your answer for Question 4. + +Implement a non-trivial, consistent heuristic for the CornersProblem in cornersHeuristic. + +python pacman.py -l mediumCorners -p AStarCornersAgent -z 0.5 + +Note: AStarCornersAgent is a shortcut for + +-p SearchAgent -a fn=aStarSearch,prob=CornersProblem,heuristic=cornersHeuristic + +Admissibility vs. Consistency: Remember, heuristics are just functions that take search states and return numbers that estimate the cost to a nearest goal. More effective heuristics will return values closer to the actual goal costs. To be admissible, the heuristic values must be lower bounds on the actual shortest path cost to the nearest goal (and non-negative). To be consistent, it must additionally hold that if an action has cost c, then taking that action can only cause a drop in heuristic of at most c. + +Remember that admissibility isn’t enough to guarantee correctness in graph search – you need the stronger condition of consistency. However, admissible heuristics are usually also consistent, especially if they are derived from problem relaxations. Therefore it is usually easiest to start out by brainstorming admissible heuristics. Once you have an admissible heuristic that works well, you can check whether it is indeed consistent, too. The only way to guarantee consistency is with a proof. However, inconsistency can often be detected by verifying that for each node you expand, its successor nodes are equal or higher in in f-value. Moreover, if UCS and A* ever return paths of different lengths, your heuristic is inconsistent. This stuff is tricky! + +Non-Trivial Heuristics: The trivial heuristics are the ones that return zero everywhere (UCS) and the heuristic which computes the true completion cost. The former won’t save you any time, while the latter will timeout the autograder. You want a heuristic which reduces total compute time, though for this assignment the autograder will only check node counts (aside from enforcing a reasonable time limit). + +Grading: Your heuristic must be a non-trivial non-negative consistent heuristic to receive any points. Make sure that your heuristic returns 0 at every goal state and never returns a negative value. Depending on how few nodes your heuristic expands, you’ll be graded: +Number of nodes expanded Grade +more than 2000 0/3 +at most 2000 1/3 +at most 1600 2/3 +at most 1200 3/3 + +Remember: If your heuristic is inconsistent, you will receive no credit, so be careful! + +Grading: Please run the below command to see if your implementation passes all the autograder test cases. + +python autograder.py -q q6 + +Q7 (4 pts): Eating All The Dots + +Now we’ll solve a hard search problem: eating all the Pacman food in as few steps as possible. For this, we’ll need a new search problem definition which formalizes the food-clearing problem: FoodSearchProblem in searchAgents.py (implemented for you). A solution is defined to be a path that collects all of the food in the Pacman world. For the present project, solutions do not take into account any ghosts or power pellets; solutions only depend on the placement of walls, regular food and Pacman. (Of course ghosts can ruin the execution of a solution! We’ll get to that in the next project.) If you have written your general search methods correctly, A* with a null heuristic (equivalent to uniform-cost search) should quickly find an optimal solution to testSearch with no code change on your part (total cost of 7). + +python pacman.py -l testSearch -p AStarFoodSearchAgent + +Note: AStarFoodSearchAgent is a shortcut for + +-p SearchAgent -a fn=astar,prob=FoodSearchProblem,heuristic=foodHeuristic + +You should find that UCS starts to slow down even for the seemingly simple tinySearch. As a reference, our implementation takes 2.5 seconds to find a path of length 27 after expanding 5057 search nodes. + +Note: Make sure to complete Question 4 before working on Question 7, because Question 7 builds upon your answer for Question 4. + +Fill in foodHeuristic in searchAgents.py with a consistent heuristic for the FoodSearchProblem. Try your agent on the trickySearch board: + +python pacman.py -l trickySearch -p AStarFoodSearchAgent + +Our UCS agent finds the optimal solution in about 13 seconds, exploring over 16,000 nodes. + +Any non-trivial non-negative consistent heuristic will receive 1 point. Make sure that your heuristic returns 0 at every goal state and never returns a negative value. Depending on how few nodes your heuristic expands, you’ll get additional points: +Number of nodes expanded Grade +more than 15000 1/4 +at most 15000 2/4 +at most 12000 3/4 +at most 9000 4/4 (full credit; medium) +at most 7000 5/4 (optional extra credit; hard) + +Remember: If your heuristic is inconsistent, you will receive no credit, so be careful! Can you solve mediumSearch in a short time? If so, we’re either very, very impressed, or your heuristic is inconsistent. + +Grading: Please run the below command to see if your implementation passes all the autograder test cases. + +python autograder.py -q q7 + +Q8 (3 pts): Suboptimal Search + +Sometimes, even with A* and a good heuristic, finding the optimal path through all the dots is hard. In these cases, we’d still like to find a reasonably good path, quickly. In this section, you’ll write an agent that always greedily eats the closest dot. ClosestDotSearchAgent is implemented for you in searchAgents.py, but it’s missing a key function that finds a path to the closest dot. + +Implement the function findPathToClosestDot in searchAgents.py. Our agent solves this maze (suboptimally!) in under a second with a path cost of 350: + +python pacman.py -l bigSearch -p ClosestDotSearchAgent -z .5 + +Hint: The quickest way to complete findPathToClosestDot is to fill in the AnyFoodSearchProblem, which is missing its goal test. Then, solve that problem with an appropriate search function. The solution should be very short! + +Your ClosestDotSearchAgent won’t always find the shortest possible path through the maze. Make sure you understand why and try to come up with a small example where repeatedly going to the closest dot does not result in finding the shortest path for eating all the dots. + +Grading: Please run the below command to see if your implementation passes all the autograder test cases. + +python autograder.py -q q8 + diff --git a/proj1/VERSION b/proj1/VERSION new file mode 100755 index 0000000..259e855 --- /dev/null +++ b/proj1/VERSION @@ -0,0 +1 @@ +v1.004 diff --git a/proj1/__pycache__/game.cpython-313.pyc b/proj1/__pycache__/game.cpython-313.pyc new file mode 100755 index 0000000000000000000000000000000000000000..505997785502ecc3be1c17a7376e6e0fdcd6d85c GIT binary patch literal 34840 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 literal 0 HcmV?d00001 diff --git a/proj1/__pycache__/graphicsUtils.cpython-313.pyc b/proj1/__pycache__/graphicsUtils.cpython-313.pyc new file mode 100755 index 0000000000000000000000000000000000000000..1195849ed0ae809f075b6bc3758463d54d24311c GIT binary patch literal 16234 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 Rq9-u+WId_oEM=^x|34?@h%x{G literal 0 HcmV?d00001 diff --git a/proj1/__pycache__/projectParams.cpython-313.pyc b/proj1/__pycache__/projectParams.cpython-313.pyc new file mode 100755 index 0000000000000000000000000000000000000000..197dfbd43256e7c01da3e3c27388d0d9a5f7bcee GIT binary patch literal 330 zcmey&%ge<81g{S0X6i9BFgylvV1NDiH`XB(=E2IVZ8WI29ycB^XeYpOu5XmEUhr}IjN&mb4yveGXq&C}0KP038uPtPzYHrIDaEJ@TafLN@bTx@7z z0p=OP!~znF5_3V$yTxIXo1apelWJGQ2{Z~60>!dG;sY}yBjX1-1_|Q}3?etgbwMP* QQUlKo9>GS=B6gq(0KIcw>Hq)$ literal 0 HcmV?d00001 diff --git a/proj1/__pycache__/search.cpython-313.pyc b/proj1/__pycache__/search.cpython-313.pyc new file mode 100755 index 0000000000000000000000000000000000000000..f63f335b29484eb36530bfc3485a2fd9d5a56eb7 GIT binary patch literal 7421 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 literal 0 HcmV?d00001 diff --git a/proj1/__pycache__/searchAgents.cpython-313.pyc b/proj1/__pycache__/searchAgents.cpython-313.pyc new file mode 100755 index 0000000000000000000000000000000000000000..ceb08955083f453dac1b14999adb885068ec105b GIT binary patch literal 31801 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 Gqx^p}oo_+_ literal 0 HcmV?d00001 diff --git a/proj1/__pycache__/testClasses.cpython-313.pyc b/proj1/__pycache__/testClasses.cpython-313.pyc new file mode 100755 index 0000000000000000000000000000000000000000..3dc673269b22d5132c991edc4ecb8d4899e0cb02 GIT binary patch literal 9629 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 Y7s&mb>7W9c^5+PK9gT8PCk+Pw19-O5x&QzG literal 0 HcmV?d00001 diff --git a/proj1/__pycache__/textDisplay.cpython-313.pyc b/proj1/__pycache__/textDisplay.cpython-313.pyc new file mode 100755 index 0000000000000000000000000000000000000000..ae91a3017938995a5ad25f1609fe4ccc5ecf41ae GIT binary patch literal 3987 zcmbtW-ESMm5#KxBQT((dONu2~A1A}IOS>ZD*s26IPU=*$BY&_m&vN1@z~M}us7t9M zy+_uynxc9NBsYN+z(V~HAbqR#)I8)5NMDLPik4lmTLUf7Cl`HCBo}TA6zI$zk0MVF zYM=w$@AhMMW_EUVc6OuD5CQtzS3~B10)+e(J1$Ap#_s#jSR*P?g-e8zKBpy6K^3W} zl3PC2M@6gK7XUqy}j? z8;V6(Bw5PkE>OKNXQu6^-5@sYtJ8=kV1E~PzeciTT}ZBzbsv*#BR9)ryQmip<~QxP zawWq?PDN4JXo8lk#JrI)^+a~=ReK~ct{3%0f#yFj(#1sDJ~cYZd#4gb~k%MK}nMM)59rRS7XMdCk!za7{LHRCTnZ=Zy4^Q9*UX zthYg%cX|Mp+SPqFx9wMs-sWdB3XL@~1L#ny-W%1iBV@)6U@~0Yf&%BseS_y&H0KRC z&zxZtSV+A*F)^iGo4hhXJ3&DGF64qDFj%$b&9 zZYluY%ZQLnc9uJW5{S8mA%Ij@>3ihp}nl7DP=oZejxM2M#xK zWV>J(nTF7TbHD@!zCYBcHSSKF0s(d#0A#gr%j)GvLGk1{3S@YqD@p57(taiHpnnw_u?HF8h<9!49J$&2!!n_w&vJc5_sL#lw6X<8ie1J-%uTe!us zcOf4Bj=X1u6s++Q$cw@b#azvsGx!>D`cCz)-y>uO-z83P5~5oiw2|vRTTH6C4p27JWi?)cKU*S%wRs?^b!^v za(VauZlt;T-TxWxX;lKd@ul$)o`Nb*OT*QZQZUkAWF00LPJD%PDg$^bsa|(x?_dAS z00&-O54qNYJ|U%eCZX$|Tn2m%97n#LLx-B-e_Mf>#1<9!EZ;1N`I8jGgy3D3;qLeoPSaf_+ zow|JPhIZvg*C$jaQ{Bo!;a1=&kVPy*r-oge%EL>`TCzrwOP$KwP>tm+im99V;MrrX zFy~xmcM^&QG>C=9Q|I2%CazDUZqz3U?Y3z#Sq=Feg3u$f>of0plp zmpf2FR?9N}2(b=N@^IeR*TE$FC}cf&<=YS4m$y1&<<8h<`-!DsrFUSfH(u_If6-d* zeeDag-1Fv=^eE6(2{&(rd&=RSUkjVzfl8!hW%BOi{nq8=CzF3}?z#K(C#iQf-o3Fg zlX;XfHoImYri`zzes*lDd+1^J&;w&DK3;e&G+@Z@bfvNdw?;mF0Whqo@>EML00c`36wVr-tC-8h~tzmnaUyS=gSi|2&=OgJxo zQ~R0F+a~0Kn8M#R@jdYwA+0lFrMYe8&hnkl#>&lu4_Y^yPgGjke-->Rcz^Iwi-PUY zr=fchcq~@NR>tmke17=z!+cN6ldHz#NYA#9bPxV(CrrAtf{l0PcPA4QeNsdHicyB& z?*Cmv(jE}wEn0Z#z}h|M$BV^_$d31Be{EJX>LtoUdK5)?OG6%itj(hrYr`wZjB)&}PW{GpG!}L&@VZk=zCqsucJ!Q$VzzyPAnf)LA+jy{gy5f~*6jc} z(7YI`L=OP8bSy?If#{_Gqk literal 0 HcmV?d00001 diff --git a/proj1/__pycache__/util.cpython-313.pyc b/proj1/__pycache__/util.cpython-313.pyc new file mode 100755 index 0000000000000000000000000000000000000000..980252384b1c7ada96c25ab630a1ed6ee97d0b0c GIT binary patch literal 28160 zcmd6Q34B!Lx%ZiUGMOZkJ%kWW_6#Ip-$DY0gs_KY2u31?NoEobOlHEF36Lll?NtMa z1W^gqN~m6iT3fNT7nfSwYU|hD*2J)cG52b#)zalEf!0!Q+wcFp?>RG*5WC!af8YJi zkG%8l+p|CKdk$A)Vk`olU%pl>?RN{p`;<^0gA%#w(FnrBfoWzGt4FROlxtvlM&y}7c}A9JMqX4X&&2X9$cw(!G)JfO zX?9rC1vyITIR>S%w^~A_(T+HjS{*ioF^+hIv5tI4Lb|YBBgZ)sk+wOK5XL)_5hgfN z5GFcO5hgj(5GFe&AWU%-IMP|0RL4Z5(;OKHC$N>K%jxWQqCAn!ZxUvZd22>!29sFs zWaNsWm1H_JJ165$bY$(!V&&N=&k2=hJ95!~jw26YZi~iVc$pSk)5Mp|L%@JNSJUzH zuyCtD5WwzhWF76gtas>SLyO*S2u4>+d)%%KPOr1#o$bglWs zBvxzLZheDNB=~#JmFW4}OEx7cc^7Y2>c)1Mu~w;MO3D)H)8M@|tglx} zA8SYEX8$isrN>I%Y?8iU*vF^-+r0O0#;AeY&UmFies$3lX~$&eZLCeuP(BGI6<>R_ zUi!)9H!E4@dow>-ql|fV>3`;-&v))Ry@D0#H*DXf6n#23y$Vft&75B#O*a}=N{PMk zZdPgg(w@20X|>tU(sdK6(v*p(cdki7e;@5X)FEZW@1DrZlfO7i>C{wdqD}tvlPnJ_ zDl4D48TIqFKE6Tvb-_*9EOT+o&N8LH@7;N2xzcpm+@DvW>UVs<6P0wsWS1XLS zad^hsmzN^_xG5n)YQFR@%UGqgvz&|2=%%mVq+{vRDZa%>_daxYwqzK7a}~?9mE>!b zL2l3cXBDc0(`^8qH2;SmG)NzJ{Wh6Z^-S434Vkm{PBTiEuKedtNq=`o1uOa5ZM&+K zN&jWpp*B=%_{!N^q~CA7a=Y|e!ioav^xF8DlJi2kTU!0jW*cjBdF9iDqAk5bp>%io z;>D74!BD2Oa$WfbR{V#e;Z$W_UurtyS9)KtXogma9$5Z^R3$pE`h1-d)vVLphT+S8 zw2R|S!ixQyl%nO!iuWl!e&de(WlHYZ%tdBQWAW5oWzutVKAz78zkg22Zl&r-=gtgt znDDR9O04K{+vUZmKlh`jx3cta^?$?w(pCHBbm_pp^SfE*zoouctMtD+y=E)QPn=20 zWkvsI?Vrs^w>OsE&eGqyjn*suTjSBq(!R~5%cNJIdy8XMnYaL(Diwb137b?tt$8D> zK4SXLHdN1Ay_jfC5RN!5Y(b^Gf>pCv`q8ZW3zU(MSKjJVqVLZ?7Oh09_r_o|rDJzL z$VvN_))}jnrG8ZTcp{26IDV;>HovsDigkFQe8NIxUaZdJ>u;!Vu2AY%<(6eC(Z)bV z2Tki&9zLzJraEG3Q`YY@Naya#EM{H3QTY+||0n0#M3#Q0^xJo!(aeHh+N2$4mQ9zQ zo6$H``o_8+Y-7cF?GMDL-OI1)QS-{Im0DKxhfQF|wxZ<>^-wcx(hO7vIzzU9KaZvMilG&awzF|VUYYTA92 zGn1s^b1K)XT(L4r89RS-u2G2|n)v%HrLV27*DXra?x{>bH+Sg&yi8hieYimKoICE3 z`rgXELwZXtUdo2DmA)Zi=s(;!hqK8q&v}>V(lNZ1^CjDa{3S>~v-Xb-tiE>pk~Z}D zgOxXBv2^2}uP#-F(}?ea4@vJX+{t(Q=jPjWNWXn`+8pU}vRBXQ&-a|%r%d|W_0e}K z(X91{)0JNCDXv+BR+Tdy+oifEx93Wkd%nb3UfbM@PLv$jZLqSgUP$>W@vYaMN#(5I zUF&60>11S;UnTnP#Xm?;M!9$IMitXjbnmD#v#IRy+m#9%7EhoJJNFHrosBRu>E|Lw z_~8@FW2CgGzwl+9XziG;4D-$Q*C#8{o|#j(D$!GWKHGs--|hP8WY){~a=*Jx8DP!4 z&r(qIwc}@INw>DY&S~-eob?)|ynM=og-Z0wf@jl|4j-8LM5~f(uex&rX87^lukDof zW@)Ff0U8r_#Vb|)*1Mxo{+TXkE-RYk*aK9RnkOIPv~|a{KkY#IQu}XHSh+U;OSIxA zdN!_-#C6{Zu*~&U&#y$LC->VcC2w(kg>=vJlR32S+qE-JnaLjv6~v79G-llSE74^&d@@elh!}Yo(0KXIDwDWS!p5 zihpB&(T_gnPg>K=(*JhbhpIX=RCk0VSJByXb0y!lhdI~&!%AnOvheh|#U5plO%q?I zHGO3J_hnK^=_6czxzhhb?P~AxpBF1tr+L>bj~?% zZnEYmweFoeg;45;v!2){_1qS(k)l4kd6~3y@L@i!RpQU9QGDqud0d;@V(n5DljW7a zU5`$Eo?mdb=AHNnbZ5zNN4h~;zVBd>6m$5k4mPP@&3h&Xoiuj*)Fa)t;jw%v>iSNb zbkCGMoaF8^n`SE0U%U2=0BTNXujazxyE#8qIfo_nhm9yQo@wXU)0KFk5b0eX_VG38 z(hIRQ(yhm~Xrx8+hCI@bj!T^B&1>D7gp!`Sq=PqSa8xR9`sO;+{`MR9StRebJA7;fXN&HlT^hOa-E@||d-wZINH=`u+bx|L zIA1EgYHEpLB@=SKPHTO>Fpg92_lrB`Vpsn8n<*2efA?+9V3iK6zlS8ktMAU>Jmjfm zCuz(rHA!4@ZCLQfQUyL@Mka~&ZzM0?A}wm|cd_>GIJL`^9gAM{wPrN!`qpP{tY~I( zo2p~~qM%IWz)vq+OY=3JnagY4lX+BCh#Knm%tz7eH@B>jj9E+fvR>XxoJQ)j*yt~m z=KSUA25F<`0H3BocHHT(XAH%%1=)gN@q<6MOOT1=@+U|9NXT>t6xk=CA57Z@EguEf3wo7 za`|;tzmSzqZ(sQ>es8HBbSf>)%ik?c zJhkLbmYFRbC_?5#>A6!_dgbjOPD1*JU)6G<6D41ZL3;h^*D|CzuQzd7w>bBva%HEc z?oEQ2lU{zy($3n?NPG%P4~s62+f7By8;|>?QNmd;%BGoS^Fn(u79U8&U>UrCgh{$$+E zlKvUrR_W64iiy&%&vuqeHBYYQM$8AhEvKh$8!*z3UicX&;1^d;fvzw8%Z1I0q?fy{?PE=~`?97g zLlxKmNVWPt&Rj`WmZRoZ)1?(XTW(@i>rzkIl&bEVnp9iPSASYnPOZ9EbZE8j;g@!@ z24|ZM&={nZr9ay)eO+$Jkb-j!rIPJmUV?qU{Ks5;Pn}puydn6rHcr+rmMtJ{?OQ*3 z>o)0xdvTNW-kWdbOD{`r-7aM>Pg@|Bt-Cj$b-uYakybPET;D9|x0aV@vdlw?ufbfA z?moG5f%Kb#Q`4k5-unVl-;bW+jOaVtr)DV-nN?r}`bfi~y?8I@LHc?BIa?`z zBJXKJw445--6_2r^CI78$AUFbq@)+pZ{ou}-uh>C?d#e;T!Q*bW*mXDq4}$B>BXO% zpD%4}dxYcB8)e^DWzxsBNyI(!pS{A-A}%B|TeL$d=eM|5=*D-JS2WN?&Stf$N;pryNst=3aSU2AVh2#cY%2yg7Wcw5d3k zYnQ|2_Ys?%_2J{QrHYT|u91f4%{H?>ZH*s8#Ioq|)B}J#i?$bCZ$bTMUt4Zt>6(&v z%ax+cmgjFp(U!%&Ns{}nA8%#liBnQlE~2wfrZvUqer1OA$NHC@Qq#XT+#=}@{XACM z^`d<_tG>Rb4g@ImU+B1%rRzI>tWzfOjcvbFwH{OB>*>m%!?jyBp~2*rKi$I`1k2w9 zK1!cWdWzecWjU{sSK+l6p2%b6w?w}PPYH{9vR!+XzQ4NQ=c=SVGry16qVfG-te0&2 zM!2!{$HnhgpqG#5R@O-A<)3m{@#(@|!mIoK&!h>`_8MIk>ojxL+Sy8{a?72x-gP4m zuOxIG;5yHY_K#H#bImbnBg*y1R&uua?y47NDpT8(5ba{{>q(`h{jUK%aX)W8P`{zxoat55!+GO8$cT-?m zlYhqi`7AYKT6;k9l(cmO%?%BbR|+&V1d}2hlqG6z4m$Q1A?Ol5(cYp-Debmg3uuMZ zNyl0rY(47h*54mJWVRlT-XDF@oH1a|=-qtYTzD1rh0_|@jGr32k;d>kTiqDL($LWA zbG3UYZf$7T+3xfxC2uYF`W6?O6+|WRamYN!zfq*PEwqvag4X5ER zMyVV_fnE)=5%JTI_X@w$-l{R!c_k`u!e8i7-L6-sBWtY7mK?Tl+Zr5jj>|@e0inqO zr?_l(nB=Gyvpp(kz?L`dxO@**r$JiwVO_F~p)eOe)Whlu9 z(e7$8f-d2MxP+eB#}*t}aByE={6JiGzd4)J-Xs*)oVLicVmXllf~HKZf~JNBm#;}7 zbUB~$X|v>M6r@m)N$L+Xf8YnVJ@M({fXIlL8BX~JLV zQ8)1Ff0gprgM!_cA3=wmW#dpjr})vNU;W=DeK`Ze$+VMWA;Vc@ESpq_?t7_X76r6( zqsU#43`Xu{i2Nh;;n0BK6^z4ak=Cy3%v$OtcIIXnrO>^k(mg9-h!rM&$?JE^0XY{# zM-n{)e9*$zKs5F!RUq(>rTO^Y+2_q7UzG|-RQn&I`fZjfcX&Z=r|gm3Ab&Ssr9$_9 z$V`tYdD)^_?Gkb0Hpy@CwYg7mpu?V)L{~bob_hS^Jkc-EgJ&|lMhRc9Ba+-l> zmKCjS9(OB*fM0ZqZAv93%iW?2+7+an+};E>>n;(OI-9luKM2zmSz+1c9M#V0_xqY8 zsBA8-E=U0pdLBcUCZL=*K#aNF54{L72L$Dofrf4u{5}{`YT5w=gC6AG<@AU?&dxo) z-KbI=ni<2oad33>Tty7v+wB+E7S6DX&ZZ`}A2`dU_IMqZSjN{@M7HIlwH0s+f$k>o z2fB;4hVcSYT(`NMZ9zkmuiYECfd-fslQIenA+B}?o%soq6m91IsG($1YAl=|5LZ*5 z5SP0jIS&hU;s&)O;A1>3j*!6NXeu4(tjGaL<{SfDqm-0f=?&LhGVOD_}GypT?4nNIO#KYA-l9|DT z(Hlk`y?}f_(Ihr2e!_{;E+8JmnBvtnP{OQ_=~7SkmRr)C`6`{3TbjZfcl@|MBWY5T3+ z@!b_eaf#j0H;RtX@`y40;|i8sg=WxhOYpMz6yhU6^;DK2r%c*QJJnSZhZ4A}qHK-FbNM4tFPf*hq)HDS(lHY_(A~`(V zN-?&#ft9+0Nu$BabH9t){+$TAgllFYYs#tIuiH=CFNm`TtQp-)k1Xh^=*=36vmNRf zN=!Ohekp0fP*U2l6-QR|y8DxIhipm5qK-sew21>Yv9It{pg(K&Ioq6JTk^4(2V;6& zC#2)jIa}c+Tk@4SA#2Vj2|`T#;T8K=^tjGj^B8sm8Hu&9CD+)a$J4vzSyX{|?O2_A z3+0jKHC9AdP#y&&6#XKCdxa}ngTXvv)k5_)>kMfl7Of#^#H=yo{LUC-$mc?znl#}r z^k|gW)m%jWmZX+VEk?U3IAPQ_-wYGGy4~AEy4K}>^wRnJzbc@aNzP1B_#c=JHw{*r z=$Bf&P7>LQ5as%3ND?zUM{I&2?|1vTibrDE<)(I!+|52&bjiLpSox60Z60S6QxGWM z64>VRLo43xi)`-}n;>uDA|P!5VipdWU6RW!Mko}`%BqUQ-P@$5Z5H>Qfb497(&1)0 z1sT@tYt9?E9bab)i=7A&XHa%O;-@xbG=j#+j?ZaR6dZMv#X{02>|*hv z(BR~qvW>0B@PnZhaF9tbg8V2=pUL;^&jn{l)?cKCiUlA}KWCmaY)-yt&Kxjj_6E+I zb2%H2Tt1N~ZNb$V1f!%B<&Jskpq-4zw8Ab8_0FKOB}*z?E~s!`uFfku5?75GcBwlN z_6me^6qsVi`2LW@mT6-?#}3N;{M{kJK8%)%ocnPUlo<6h4Mz)kc(zmQ@Z7 zp<%6t{}x-qZi`6whC{2=V!x5K$o_z9L)W<=1}e0jqbp1 zx7!P68?dlP%tA5S?FHyEAuWaX*EQ|nKsJeTvF^GyEf$%DD21JR}Z`cjU$ zXdHS%3~?*6{uV=wbVYL01);?eg)rKI`?$DY8;vlA-NcP`;0`YC*2W^VIpXB_7Mndj zXsRU5+bv)IHO6yR>S5kgz=6r~#2s#T8+nT0vm>_1vi*$LIYl_1DQ}0n!(U>tY{ZtU ztr(Xi?ug9?&vJYHWCkm?3BkhUlwD$52l%4b;*uEWcFG-MprZ{2lemHzJpMK}j3STJ z3FZn4+veQm79qbI!7*X!w7dP5Lb8Qg+_GN;?``(@oB_YRh(T2HHhJ2?nnfqtd0oxz z%rbJiToMThr^ljlR>pbVPU6m(f57PvFO$5oWFOIlfHF=Q9_MzCQ!cWAZL?L2P6!K^ zyV=?9A=^!zB%7T$9qnrU)QJ-as4lV8VsVP);_Z7aUR-{ET)cX)lf39_n(8(t9Z`cPnBWT9G3 zSO9m7v@2U1-U89z0q6Lh&z*Q^OR%Jw<1b|4uocleUnr-0w#qm1_6n?ZP_R8<~0lR7BHOAf?m>_W>bhiyOVmXzsvT8{M$xS@$kCPANRlX5g`M zcTji0ZL9%(dVO-MLVSuZ0y`G)1>j>~!!t5-$}M86bB{U?MLlu5#;77-DO?t>V;l{# zR^@R>s}HgNu+sk5Rg}6=O<0Xp=mFXXz!*?)I;baX3r06IVBMsuG&Cp*NKhxa_C&IC zSw`z%VkO^Es2iaciI}553#BlaVGzgdkL%U-u0Lmdle+~hv}VBC~`^Ayg) zb?QR!OM;5hSIG*5Q&j_tD~_zKI$gQ8oXasSa|Y=A+5*&B6E84phUr^JEi1fD@ELOV z5_pYU^))HnR%`^T1h)7|e`U}B{~ezTI7@(!ysrw6r?9@j=XDnp6+jg&C=v?-+hn)9 zU`vrWgYyiK9+htaVzchRoKTUHHBA49mx9!|pq_X>cUb5sN&pTUL)OS4gGuV%M&_o{ z=BA@nmvA*jNJ{Ij9J1ODuINo3ux1`wF``2j-_=M&3GM3JXgFGf*buqbmP3n?p5f4%y;BQ$R-4dz5U}w2MkYK${YS~84XC_=q zEZmXK6&3?`Hta&LuvCHWe)$%P>p+@z1BaLnGApRr6Et$s88oA81sSz|nhe2CwPZol zYn*$6u`1YjIuFb6--fEphPB3Z2gv<=@bXy7*>2!r|}AUta< zgaH>_2tqmrp?5BzP5NgC2098dq>3g&Z zN!v~bbXUp6Hj3aBRlQb0<49#39C-z*Lygvx*aG9-hh$LG5y^Ig<}#17wbA8V6wFl- zEj-DRGLH|ov40UFyjB^ZA9svBsqb6gxAWx2Qx&J2r>joZpA%;=RzZ4uQ)`>zRhDn3 zLG{$9%Cw^S04s<4D^xl+o7@=wWeLwDm~i9lrqNs$qkG(bNE1>g9@~Co`%zDK z^reIu-KHUPqH-L4b3YwR=UvLQLrQ#cM{j&j)YR`r7&{;)#mA$aj>d|b(-`+Y_byxJ)5(`;5D3|ZVU}GJXK?@W2Yy-mk5YD?P z3=_DuY>7DSU3g^S(elB#tbTLWjqE$LvVZz7t&EuR-_OFqw5<@h+B(H(EQZ{w5vKW@G5jY3b8{k9RkGQH%wCk z;6j>`rZ|ljFpj1)*-|Bu&kQ;;Hkbj>Ps))CxgcUoja?%@6tN*DrC_`*p(2+XN>X@B zLRqA*`g8b}n1#f&UenQ%Zqqx7sjA+RI$#z5w%)R6%*y||k{kBJjUw*J5fZ4$*Vdt$ z)e8Dr0Q^>rBa_29sHYlZ30wXW6Hd1h~td% zDHBw{5>o+iCxYyCbu$*CmJ=+Xj#*-LiG;Jo_^ZodHZ=4@rG%$(_{-P?S>HC+!*tez z%3fG|rENk4OVKzr#8MOtVW&2#!-aE`FouNmdN2*{;Uq!C6a;upEQ}|^JE`2U<2b>* zHH2uROBrzf*AS)9DNYp|?IAt{lmE1a36j&?=4U8^>zZ&id0m`kjK&dg42~iZ9D`-t zPZ)ADzy#TnAtX#VI-%RhMJHIpo}tX_JN& z?ZiaVCmzr5u0FUb#5+>DV{Vj9A+&k`qy87sipih9iB{k#K+I8S<(f$WH@o@+kyk3^Sk@ z7BpHYv5cn(#p?fuP%NW&=kZ;~r;dwe%nMdyPx$=vGx8_OdnmYzfW=s-7|v$s+n1;56hzX}1JxaH%Vy+)%oOdT;>amO{oY!FA1;*iYN z8?r~N7Q>X0G@~JF#HKeCj#$$Si6gUZsF#~&Ncr7t1dL`_w@v6V^w3QV#*YPTCxMKx5tXz&sS}rW)p7yG(1B$<5c9F=fgWaSeRwJ{KJ1FvnE?z1vkPc1DRf zBcWFn#7;f&e0<-Ns-2~-eyb+tOh9{>Q+{V}u)IbPNA!){Oq4 zTx$s#-=b5sHq^5$D5Z);%M?~-qA(pTbPB-zJEVAU-A&Q?jf}p7dOB2PR&dxsz?6Ty zwGzk8pS7~0)dk~26LG0o@RYHmVEJKe0~7r66Ntf+%uJ`C#vjzWJ$~AGQW)98FXl0h zN5Pwfr^M@+bvZ$NG^$^pJZy^YUOQ;Ycv9b=H+N8+H)xtSl#tbJ!p)|hmWwHc11W`r zDN_b*Q$s$7-j<$yLy76+MSf%vl!By*M^<+)9m<<>a!bE;(x5f_ia|)q{=_KQ;D8d} zix<0%!`AqRa(bGNP93ync58;Btv#B9b9?i9$}eRV48|3HIi+`I&-#aqCsPLE3j584 zaLX0?>7v5-qGOk2=ttePBdlS(k^Cc(Xdgy#AOm!0C%_$n?4k1qaK;d~s@1AUG|CGN z!xknqO@KY)T{I1IU7%DtM_aER#GwCc$S(@hfbtIbzTvWE9JlR0-1%!Ph2HCgr?IY@ zrdDfaX}ls)Yvh+zy~bL@h7buT)51?(D%DrlAPQ@(L<-D2H`R63ny8ee%*q&~b7P!Z zLkRWZPd}~W9G3fRjoc(9gb5n(q8kqONgl}uD9AwogtgLD4))3y)NSUq3~Idct<*$M z@5Tj9I1|Geu0I&XBkO$d^-?2>=$c&3;~;9#u}zbQ!}2d8;maba$TS1{_4YP4y<`#?a8{Xs{ZJbA-(y&6?d)p zr&U9-wytGEnc%Q}WzT6&F7DNJtr)T-9xNV;j@!Su*MUQmA!~AP{QjEm6+_m?%)~+j)V| z6ui9r*$|4UIg#H(^Xel|-%+=e`JbS@lMG>qs;gU9 z%ajV1GAK0&ZBdJB^pU8m9NPhj<<~JDu8h8dAI@=&{QN+^opP9)g>gbcX1h`T0p&7( zJ1b|nYi!!)lbYO&#WMaA!A65dj>+->8vdG)lXOKw&gkydgQf|O-8^W@9k!+P+;K70 zK9Fi3Or1JN_qs3TPaRAx`m(z(zBlVJ{YiJXwLi7!l9)FbpZ{fpax!P2lezrQL-UwK zt|)X>55>myWb`_ZOzvtQve*um44I?4*Y7uiqncwVd%!Gq8$K}z(eZ~$z<;d?`xo@< zldg{>3TegHgYo%(g58u+HV9Pk7#yB<6zAhV31 zW=Bx7Ym7w`&tY4QcLx=DD_=mn>jdUyhycvigq~UCXg|7e&^ozm>2P%XNyE7W!0s8@@-)eJE@*m)5Nd-4zGX)y?Jvv_|!z;LH?Cjmxqu zWu^k7{GE(5rxTApEN0Hp!g3BDF>5`gL7+K&iQuG{qiRB~A znLTv+*D1AuzMeojg2+f~!Aqo=Ku`zu=mzSF#f6V`;pxf4sQ)M0{$fPn)3OLLHtc$G zT5raM?tN+ZK68LmxwqhK?Bf*N`BF>13%YOBO;M)_Ton$bvkLu}X_dpPDm%)z*> z3TQq1=ZrT!ni`wl>xhgdJXNxf785S8!U$)6NdDIo4zY&`6ViV|0wm0=bA@_hf$SOo z&c7;Ryg2AnJb%C`hI6y)au}9r>k{bA1ob_IT0Jr1T<+MVR}d>UI(NX|@5YsIhFXg9 zN*AKS2lj=84n^dqvx{v`9J{m2Oq>+`@(^ZN16Mg?=sS^MT%ECBrjVeB@$i2~QBdEA zyMuC&q9*m44zZ)K8XQakbI0{>qVcLo?d5$`!&k8cKVbvjmsR=s#z9lolN(PJ4d%^z zW!_-klHs`I9?xK0PQN*46jWoos@|E9w?76LX}02(79ghe(1PBZAN1lXMLNH3Kk60* zL5abeQ+h4m*@Os)V&4hvRp>`@0^FMzU8b*_J)0fCbS|`16udl_oc`FX-u6dJFC^!~6oulm=Au;`u!?=j zCo?WsORnmKl!7ZdAt9BiMO|yIk3)X@S`L4p`#oUNQ_0OXIfU?w8FodnDQS{Jc}SsFFY|f zMM5(C29t1IZPTd=yfr9J@6H{v#zGZ{OX_hwu(w+`jC%mNkCfBtMDqUIduP#&&^hPh z3WxA&mi36WPuJ_YU@N)`pF|cgz%~htC#)u5sL(@FkOmHD% zhzm_71gwuyYt_RU$DbQrZVp38^Tg?1tr46nOW|B5_$o~3*AVJA6Sp?k)f1y+Ppv`a zKnsy(=uP@ec=qem*`rrc>D3HJ;awY0Z|tM$Dx+2cdEjeS-cu1mGsVi_Phq5mo|Aab za042dMq{o|R{O3st21;MnI;8ltTprgpgoOW?;*wGl&bcEPqiyd!mX(f=bCGUVlbF+ znZj|6iXo%*8bUGT*`qp}Ez9tR2Z)zDyvZR+*VENgD!hu`+Qx4^^3V710=iB`pYJKL z)cTz*?o!d?^X+JFD^%~mlc&2#Z1KP%@ZdTk-m=$}Fb7Z#lYX6XW#q6F3wWOeiNrX=LI0=E2ythU4y=GFenFi};&vNjI>D%=dz##B^zsP2PR)$x8XfM+&A|H z{6Pz!GR-t-@-=Q}5hKqG>W~pM@dR8_KGv6iJa3edZ zz1cXhXnoJJ;rNNYD+l8X;1-Qb>Dk3^_Pm!p^I*?A}?g=w7LM zE%D_QFUR(;tRI+Pe{9x3_Uvv`f5NSo<}Dgb*!2AFQ=9%Z=gsCHPaRlNJD6Db(7fKc z7ZMB4?4~-KhRktK`%e1K<TT=jPTyk7s=oiEOPZR3lJj#l^Je#k@B1Nrj?5?2kF zSHathdn3CKmi5jUjLzu0`PBN8oBK1TshL^0UHs&N{^H8PoT~mwRl~8V{b>u&x(3pg z48~UWn=7x6L<@;qG}nU(o2~~FSNS1@gzwJHsG6hyUW$>z?1ZXP{rBd_A^iSCYt>@o z_oqf7Y*I;ueu?uk?wgKCq|gm>OXafQOv^ea-ZbHgE_8`DyjB8yaXVW@=WZv|TSzaI z<0h}$?P|nvPl=^dEL#Wd0|p7bVKt4vf5XrIy{O;rUWA(%q>-R6ruXrKIpT#lE27b4 z8=#L5K?;7#WRl5X>9-V}*B5hb54oX7p?l#f6f&%3^nW}M@!t-lZIoKwSb_RNX9&3^ zeDzM~oE%EiRN2WBX{}B%z?&(jpvKQX1x>;DW7Z=lV0J8^F_dp|UDDfwdNW@%m5+i< zXf^rctOl>Jjrl5<@@{b4n6*hQ7;cou18ENN4AyU*=)o_4@RqDPDoy~oQ%4&#DeJ~j zo$~Uga>G)l_ZZ|91b#vx=okFtBNmC^*i8<-%esx$5620hIh2}tzjY{0Jfz%WpkCA` zG)>yfNW@e{k6XhhsA0RRUivhWVbR`-OQACHr^tB)W9el$d}mEvPv)4tDj8jlPZ!vzT~A2Ho(-XxnlsAtZTP#^5$fZw_f~75yZ*2jwr&___m~w7 zB?u#i96;eY;s0V0RQQMjtsyHC#{bRzYZl6NlZ9rDT~`w{IaVyISiNBlKUgy3VR^K^#txJelY=0|5+0ywcgqXqZtm&P&1Mlutke^?3UQ(i)zk3X14_9+&(vR)piF z0A2_s@(D&$gQXs4o8Rra(agZrIeN_>n#MT%fS2HF@PeQGj*Poo6Caw}yWnDU-avHT za6;O#;s=YLG(H`BGWI$B#p#s;(<=uPs=6!T7CyZC!0O(l6B)-d&RO$6(duGm4NaJI zV$v5UoznMDnE#w8t8bo_%WBK=SPF>f!Xv6ZS9KADwzCY9M_^_o|_|^b?-ra7j(U z$0;t_at3TUeO0G&&)KHI66%im!?k!JJ~eXG^R}6PWDM`S`3Y62!t1F8Rf}}+Xok4w z=e?0mNXft?+Zm&!Bx$r^DJdsW0R_b7nOEdqVMMFN-4}d#!u~sBf_mA7>Qs*}U%2ES zEiJixQU04A>&^HGl(P}nG}=8LX+K69b>4=axj-Q{^{^^9*i6;;4vlndiT6ORuDidwZye3MimCGvuW2U}_9p+^~{S zTSC(#iE*_V0eCq={)G58`f7pExigY-$KlR9BTn;Fc@^exBv|;v{+FSRaM&0d*3@oX zS|z`Le2{6t1@$CoYgoHs#j+JO6{{N@8>`mVZe%kag95>*R`_-Bkp%eqh zF5x3n%-y?&%&|i;@mv8(zXZROqE8kl4o(qT>(DWlKoF z65_K$(^h2vMtdIb_<8Dm6$K;#G-__Q!rlYd1(aBa0FR#T*v+xtMUZcNeb?7uH-L7l+U(qxv zBioHejcvphud$Eh$7&L14>NO=-GX+iTJ3{m|%q*(6b07fE2tJj-HTUKj2iyk%>N6$+;6M^ literal 0 HcmV?d00001 diff --git a/proj1/autograder.py b/proj1/autograder.py new file mode 100755 index 0000000..05584cf --- /dev/null +++ b/proj1/autograder.py @@ -0,0 +1,366 @@ +# autograder.py +# ------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, +# Pieter Abbeel (pabbeel@cs.berkeley.edu), and edited by Noemi Chulo. + + +# imports from python standard library +import grading +import importlib.util +import optparse +import os +import re +import sys +import projectParams +import random +random.seed(0) +try: + from pacman import GameState +except: + pass + +# register arguments and set default values +def readCommand(argv): + parser = optparse.OptionParser(description = 'Run public tests on student code') + parser.set_defaults(generateSolutions=False, edxOutput=False, gsOutput=False, muteOutput=False, printTestCase=False, noGraphics=False) + parser.add_option('--test-directory', + dest = 'testRoot', + default = 'test_cases', + help = 'Root test directory which contains subdirectories corresponding to each question') + parser.add_option('--student-code', + dest = 'studentCode', + default = projectParams.STUDENT_CODE_DEFAULT, + help = 'comma separated list of student code files') + parser.add_option('--code-directory', + dest = 'codeRoot', + default = "", + help = 'Root directory containing the student and testClass code') + parser.add_option('--test-case-code', + dest = 'testCaseCode', + default = projectParams.PROJECT_TEST_CLASSES, + help = 'class containing testClass classes for this project') + parser.add_option('--generate-solutions', + dest = 'generateSolutions', + action = 'store_true', + help = 'Write solutions generated to .solution file') + parser.add_option('--edx-output', + dest = 'edxOutput', + action = 'store_true', + help = 'Generate edX output files') + parser.add_option('--gradescope-output', + dest = 'gsOutput', + action = 'store_true', + help = 'Generate GradeScope output files') + parser.add_option('--mute', + dest = 'muteOutput', + action = 'store_true', + help = 'Mute output from executing tests') + parser.add_option('--print-tests', '-p', + dest = 'printTestCase', + action = 'store_true', + help = 'Print each test case before running them.') + parser.add_option('--test', '-t', + dest = 'runTest', + default = None, + help = 'Run one particular test. Relative to test root.') + parser.add_option('--question', '-q', + dest = 'gradeQuestion', + default = None, + help = 'Grade one particular question.') + parser.add_option('--no-graphics', + dest = 'noGraphics', + action = 'store_true', + help = 'No graphics display for pacman games.') + (options, args) = parser.parse_args(argv) + return options + + +# confirm we should author solution files +def confirmGenerate(): + print('WARNING: this action will overwrite any solution files.') + print('Are you sure you want to proceed? (yes/no)') + while True: + ans = sys.stdin.readline().strip() + if ans == 'yes': + break + elif ans == 'no': + sys.exit(0) + else: + print('please answer either "yes" or "no"') + + +# TODO: Fix this so that it tracebacks work correctly +# Looking at source of the traceback module, presuming it works +# the same as the intepreters, it uses co_filename. This is, +# however, a readonly attribute. +def setModuleName(module, filename): + functionType = type(confirmGenerate) + classType = type(optparse.Option) + + for i in dir(module): + o = getattr(module, i) + if hasattr(o, '__file__'): continue + + if type(o) == functionType: + setattr(o, '__file__', filename) + elif type(o) == classType: + setattr(o, '__file__', filename) + # TODO: assign member __file__'s? + #print(i, type(o)) + + +#from cStringIO import StringIO + +def loadModuleString(moduleSource): + # Below broken, imp doesn't believe its being passed a file: + # ValueError: load_module arg#2 should be a file or None + # + #f = StringIO(moduleCodeDict[k]) + #tmp = imp.load_module(k, f, k, (".py", "r", imp.PY_SOURCE)) + tmp = imp.new_module(k) + exec(moduleCodeDict[k] in tmp.__dict__) + setModuleName(tmp, k) + return tmp + +import py_compile + +def loadModuleFile(moduleName, filePath): + # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly + spec = importlib.util.spec_from_file_location(moduleName, filePath) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def readFile(path, root=""): + "Read file from disk at specified path and return as string" + with open(os.path.join(root, path), 'r') as handle: + return handle.read() + + +####################################################################### +# Error Hint Map +####################################################################### + +# TODO: use these +ERROR_HINT_MAP = { + 'q1': { + "": """ + We noticed that your project threw an IndexError on q1. + While many things may cause this, it may have been from + assuming a certain number of successors from a state space + or assuming a certain number of actions available from a given + state. Try making your code more general (no hardcoded indices) + and submit again! + """ + }, + 'q3': { + "": """ + We noticed that your project threw an AttributeError on q3. + While many things may cause this, it may have been from assuming + a certain size or structure to the state space. For example, if you have + a line of code assuming that the state is (x, y) and we run your code + on a state space with (x, y, z), this error could be thrown. Try + making your code more general and submit again! + + """ + } +} + +import pprint + +def splitStrings(d): + d2 = dict(d) + for k in d: + if k[0:2] == "__": + del d2[k] + continue + if d2[k].find("\n") >= 0: + d2[k] = d2[k].split("\n") + return d2 + + +def printTest(testDict, solutionDict): + pp = pprint.PrettyPrinter(indent=4) + print("Test case:") + for line in testDict["__raw_lines__"]: + print(" |", line) + print("Solution:") + for line in solutionDict["__raw_lines__"]: + print(" |", line) + + +def runTest(testName, moduleDict, printTestCase=False, display=None): + import testParser + import testClasses + for module in moduleDict: + setattr(sys.modules[__name__], module, moduleDict[module]) + + testDict = testParser.TestParser(testName + ".test").parse() + solutionDict = testParser.TestParser(testName + ".solution").parse() + test_out_file = os.path.join('%s.test_output' % testName) + testDict['test_out_file'] = test_out_file + testClass = getattr(projectTestClasses, testDict['class']) + + questionClass = getattr(testClasses, 'Question') + question = questionClass({'max_points': 0}, display) + testCase = testClass(question, testDict) + + if printTestCase: + printTest(testDict, solutionDict) + + # This is a fragile hack to create a stub grades object + grades = grading.Grades(projectParams.PROJECT_NAME, [(None,0)]) + testCase.execute(grades, moduleDict, solutionDict) + + +# returns all the tests you need to run in order to run question +def getDepends(testParser, testRoot, question): + allDeps = [question] + questionDict = testParser.TestParser(os.path.join(testRoot, question, 'CONFIG')).parse() + if 'depends' in questionDict: + depends = questionDict['depends'].split() + for d in depends: + # run dependencies first + allDeps = getDepends(testParser, testRoot, d) + allDeps + return allDeps + +# get list of questions to grade +def getTestSubdirs(testParser, testRoot, questionToGrade): + problemDict = testParser.TestParser(os.path.join(testRoot, 'CONFIG')).parse() + if questionToGrade != None: + questions = getDepends(testParser, testRoot, questionToGrade) + if len(questions) > 1: + print('Note: due to dependencies, the following tests will be run: %s' % ' '.join(questions)) + return questions + if 'order' in problemDict: + return problemDict['order'].split() + return sorted(os.listdir(testRoot)) + + +# evaluate student code +def evaluate(generateSolutions, testRoot, moduleDict, exceptionMap=ERROR_HINT_MAP, + edxOutput=False, muteOutput=False, gsOutput=False, + printTestCase=False, questionToGrade=None, display=None): + # imports of testbench code. note that the testClasses import must follow + # the import of student code due to dependencies + import testParser + import testClasses + for module in moduleDict: + setattr(sys.modules[__name__], module, moduleDict[module]) + + questions = [] + questionDicts = {} + questionTimeouts = {} # grader timeouts for each question + test_subdirs = getTestSubdirs(testParser, testRoot, questionToGrade) + + for q in test_subdirs: + subdir_path = os.path.join(testRoot, q) + if not os.path.isdir(subdir_path) or q[0] == '.': + continue + + # create a question object + questionDict = testParser.TestParser(os.path.join(subdir_path, 'CONFIG')).parse() + questionClass = getattr(testClasses, questionDict['class']) + question = questionClass(questionDict, display) + questionDicts[q] = questionDict + timeout = question.getTimeout() + if timeout: + questionTimeouts[q] = timeout + + # load test cases into question + tests = filter(lambda t: re.match(r'[^#~.].*\.test\Z', t), os.listdir(subdir_path)) + tests = map(lambda t: re.match(r'(.*)\.test\Z', t).group(1), tests) + for t in sorted(tests): + test_file = os.path.join(subdir_path, '%s.test' % t) + solution_file = os.path.join(subdir_path, '%s.solution' % t) + test_out_file = os.path.join(subdir_path, '%s.test_output' % t) + testDict = testParser.TestParser(test_file).parse() + if testDict.get("disabled", "false").lower() == "true": + continue + testDict['test_out_file'] = test_out_file + testClass = getattr(projectTestClasses, testDict['class']) + testCase = testClass(question, testDict) + def makefun(testCase, solution_file): + if generateSolutions: + # write solution file to disk + return lambda grades: testCase.writeSolution(moduleDict, solution_file) + else: + # read in solution dictionary and pass as an argument + testDict = testParser.TestParser(test_file).parse() + solutionDict = testParser.TestParser(solution_file).parse() + if printTestCase: + return lambda grades: printTest(testDict, solutionDict) or testCase.execute(grades, moduleDict, solutionDict) + else: + return lambda grades: testCase.execute(grades, moduleDict, solutionDict) + question.addTestCase(testCase, makefun(testCase, solution_file)) + + # Note extra function is necessary for scoping reasons + def makefun(question): + return lambda grades: question.execute(grades) + setattr(sys.modules[__name__], q, makefun(question)) + questions.append((q, question.getMaxPoints())) + + grades = grading.Grades(projectParams.PROJECT_NAME, questions, + gsOutput=gsOutput, edxOutput=edxOutput, muteOutput=muteOutput, questionTimeouts=questionTimeouts) + if questionToGrade == None: + for q in questionDicts: + for prereq in questionDicts[q].get('depends', '').split(): + grades.addPrereq(q, prereq) + + grades.grade(sys.modules[__name__], bonusPic = projectParams.BONUS_PIC) + return grades.points + + + +def getDisplay(graphicsByDefault, options=None): + graphics = graphicsByDefault + if options is not None and options.noGraphics: + graphics = False + if graphics: + try: + import graphicsDisplay + return graphicsDisplay.PacmanGraphics(1, frameTime=.05) + except ImportError: + pass + import textDisplay + return textDisplay.NullGraphics() + + + + +if __name__ == '__main__': + options = readCommand(sys.argv) + if options.generateSolutions: + confirmGenerate() + codePaths = options.studentCode.split(',') + # moduleCodeDict = {} + # for cp in codePaths: + # moduleName = re.match(r'.*?([^/]*)\.py', cp).group(1) + # moduleCodeDict[moduleName] = readFile(cp, root=options.codeRoot) + # moduleCodeDict['projectTestClasses'] = readFile(options.testCaseCode, root=options.codeRoot) + # moduleDict = loadModuleDict(moduleCodeDict) + + moduleDict = {} + for cp in codePaths: + moduleName = re.match(r'.*?([^/]*)\.py', cp).group(1) + moduleDict[moduleName] = loadModuleFile(moduleName, os.path.join(options.codeRoot, cp)) + moduleName = re.match(r'.*?([^/]*)\.py', options.testCaseCode).group(1) + moduleDict['projectTestClasses'] = loadModuleFile(moduleName, os.path.join(options.codeRoot, options.testCaseCode)) + + + if options.runTest != None: + runTest(options.runTest, moduleDict, printTestCase=options.printTestCase, display=getDisplay(True, options)) + else: + evaluate(options.generateSolutions, options.testRoot, moduleDict, + gsOutput=options.gsOutput, + edxOutput=options.edxOutput, muteOutput=options.muteOutput, printTestCase=options.printTestCase, + questionToGrade=options.gradeQuestion, display=getDisplay(options.gradeQuestion!=None, options)) diff --git a/proj1/eightpuzzle.py b/proj1/eightpuzzle.py new file mode 100755 index 0000000..bb8ea6f --- /dev/null +++ b/proj1/eightpuzzle.py @@ -0,0 +1,281 @@ +# eightpuzzle.py +# -------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +import search +import random + +# Module Classes + +class EightPuzzleState: + """ + The Eight Puzzle is described in the course textbook on + page 64. + + This class defines the mechanics of the puzzle itself. The + task of recasting this puzzle as a search problem is left to + the EightPuzzleSearchProblem class. + """ + + def __init__( self, numbers ): + """ + Constructs a new eight puzzle from an ordering of numbers. + + numbers: a list of integers from 0 to 8 representing an + instance of the eight puzzle. 0 represents the blank + space. Thus, the list + + [1, 0, 2, 3, 4, 5, 6, 7, 8] + + represents the eight puzzle: + ------------- + | 1 | | 2 | + ------------- + | 3 | 4 | 5 | + ------------- + | 6 | 7 | 8 | + ------------ + + The configuration of the puzzle is stored in a 2-dimensional + list (a list of lists) 'cells'. + """ + self.cells = [] + numbers = numbers[:] # Make a copy so as not to cause side-effects. + numbers.reverse() + for row in range( 3 ): + self.cells.append( [] ) + for col in range( 3 ): + self.cells[row].append( numbers.pop() ) + if self.cells[row][col] == 0: + self.blankLocation = row, col + + def isGoal( self ): + """ + Checks to see if the puzzle is in its goal state. + + ------------- + | | 1 | 2 | + ------------- + | 3 | 4 | 5 | + ------------- + | 6 | 7 | 8 | + ------------- + + >>> EightPuzzleState([0, 1, 2, 3, 4, 5, 6, 7, 8]).isGoal() + True + + >>> EightPuzzleState([1, 0, 2, 3, 4, 5, 6, 7, 8]).isGoal() + False + """ + current = 0 + for row in range( 3 ): + for col in range( 3 ): + if current != self.cells[row][col]: + return False + current += 1 + return True + + def legalMoves( self ): + """ + Returns a list of legal moves from the current state. + + Moves consist of moving the blank space up, down, left or right. + These are encoded as 'up', 'down', 'left' and 'right' respectively. + + >>> EightPuzzleState([0, 1, 2, 3, 4, 5, 6, 7, 8]).legalMoves() + ['down', 'right'] + """ + moves = [] + row, col = self.blankLocation + if(row != 0): + moves.append('up') + if(row != 2): + moves.append('down') + if(col != 0): + moves.append('left') + if(col != 2): + moves.append('right') + return moves + + def result(self, move): + """ + Returns a new eightPuzzle with the current state and blankLocation + updated based on the provided move. + + The move should be a string drawn from a list returned by legalMoves. + Illegal moves will raise an exception, which may be an array bounds + exception. + + NOTE: This function *does not* change the current object. Instead, + it returns a new object. + """ + row, col = self.blankLocation + if(move == 'up'): + newrow = row - 1 + newcol = col + elif(move == 'down'): + newrow = row + 1 + newcol = col + elif(move == 'left'): + newrow = row + newcol = col - 1 + elif(move == 'right'): + newrow = row + newcol = col + 1 + else: + raise "Illegal Move" + + # Create a copy of the current eightPuzzle + newPuzzle = EightPuzzleState([0, 0, 0, 0, 0, 0, 0, 0, 0]) + newPuzzle.cells = [values[:] for values in self.cells] + # And update it to reflect the move + newPuzzle.cells[row][col] = self.cells[newrow][newcol] + newPuzzle.cells[newrow][newcol] = self.cells[row][col] + newPuzzle.blankLocation = newrow, newcol + + return newPuzzle + + # Utilities for comparison and display + def __eq__(self, other): + """ + Overloads '==' such that two eightPuzzles with the same configuration + are equal. + + >>> EightPuzzleState([0, 1, 2, 3, 4, 5, 6, 7, 8]) == \ + EightPuzzleState([1, 0, 2, 3, 4, 5, 6, 7, 8]).result('left') + True + """ + for row in range( 3 ): + if self.cells[row] != other.cells[row]: + return False + return True + + def __hash__(self): + return hash(str(self.cells)) + + def __getAsciiString(self): + """ + Returns a display string for the maze + """ + lines = [] + horizontalLine = ('-' * (13)) + lines.append(horizontalLine) + for row in self.cells: + rowLine = '|' + for col in row: + if col == 0: + col = ' ' + rowLine = rowLine + ' ' + col.__str__() + ' |' + lines.append(rowLine) + lines.append(horizontalLine) + return '\n'.join(lines) + + def __str__(self): + return self.__getAsciiString() + +# TODO: Implement The methods in this class + +class EightPuzzleSearchProblem(search.SearchProblem): + """ + Implementation of a SearchProblem for the Eight Puzzle domain + + Each state is represented by an instance of an eightPuzzle. + """ + def __init__(self,puzzle): + "Creates a new EightPuzzleSearchProblem which stores search information." + self.puzzle = puzzle + + def getStartState(self): + return puzzle + + def isGoalState(self,state): + return state.isGoal() + + def getSuccessors(self,state): + """ + Returns list of (successor, action, stepCost) pairs where + each succesor is either left, right, up, or down + from the original state and the cost is 1.0 for each + """ + succ = [] + for a in state.legalMoves(): + succ.append((state.result(a), a, 1)) + return succ + + def getCostOfActions(self, actions): + """ + actions: A list of actions to take + + This method returns the total cost of a particular sequence of actions. The sequence must + be composed of legal moves + """ + return len(actions) + +EIGHT_PUZZLE_DATA = [[1, 0, 2, 3, 4, 5, 6, 7, 8], + [1, 7, 8, 2, 3, 4, 5, 6, 0], + [4, 3, 2, 7, 0, 5, 1, 6, 8], + [5, 1, 3, 4, 0, 2, 6, 7, 8], + [1, 2, 5, 7, 6, 8, 0, 4, 3], + [0, 3, 1, 6, 8, 2, 7, 5, 4]] + +def loadEightPuzzle(puzzleNumber): + """ + puzzleNumber: The number of the eight puzzle to load. + + Returns an eight puzzle object generated from one of the + provided puzzles in EIGHT_PUZZLE_DATA. + + puzzleNumber can range from 0 to 5. + + >>> print(loadEightPuzzle(0)) + ------------- + | 1 | | 2 | + ------------- + | 3 | 4 | 5 | + ------------- + | 6 | 7 | 8 | + ------------- + """ + return EightPuzzleState(EIGHT_PUZZLE_DATA[puzzleNumber]) + +def createRandomEightPuzzle(moves=100): + """ + moves: number of random moves to apply + + Creates a random eight puzzle by applying + a series of 'moves' random moves to a solved + puzzle. + """ + puzzle = EightPuzzleState([0,1,2,3,4,5,6,7,8]) + for i in range(moves): + # Execute a random legal move + puzzle = puzzle.result(random.sample(puzzle.legalMoves(), 1)[0]) + return puzzle + +if __name__ == '__main__': + puzzle = createRandomEightPuzzle(25) + print('A random puzzle:') + print(puzzle) + + problem = EightPuzzleSearchProblem(puzzle) + path = search.breadthFirstSearch(problem) + print('BFS found a path of %d moves: %s' % (len(path), str(path))) + curr = puzzle + i = 1 + for a in path: + curr = curr.result(a) + print('After %d move%s: %s' % (i, ("", "s")[i>1], a)) + print(curr) + + input("Press return for the next state...") # wait for key stroke + i += 1 diff --git a/proj1/game.py b/proj1/game.py new file mode 100755 index 0000000..3950f27 --- /dev/null +++ b/proj1/game.py @@ -0,0 +1,729 @@ +# game.py +# ------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +# game.py +# ------- +# Licensing Information: Please do not distribute or publish solutions to this +# project. You are free to use and extend these projects for educational +# purposes. The Pacman AI projects were developed at UC Berkeley, primarily by +# John DeNero (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# For more info, see http://inst.eecs.berkeley.edu/~cs188/sp09/pacman.html + +from util import * +import time, os +import traceback +import sys + +####################### +# Parts worth reading # +####################### + +class Agent: + """ + An agent must define a getAction method, but may also define the + following methods which will be called if they exist: + + def registerInitialState(self, state): # inspects the starting state + """ + def __init__(self, index=0): + self.index = index + + def getAction(self, state): + """ + The Agent will receive a GameState (from either {pacman, capture, sonar}.py) and + must return an action from Directions.{North, South, East, West, Stop} + """ + raiseNotDefined() + +class Directions: + NORTH = 'North' + SOUTH = 'South' + EAST = 'East' + WEST = 'West' + STOP = 'Stop' + + LEFT = {NORTH: WEST, + SOUTH: EAST, + EAST: NORTH, + WEST: SOUTH, + STOP: STOP} + + RIGHT = dict([(y,x) for x, y in LEFT.items()]) + + REVERSE = {NORTH: SOUTH, + SOUTH: NORTH, + EAST: WEST, + WEST: EAST, + STOP: STOP} + +class Configuration: + """ + A Configuration holds the (x,y) coordinate of a character, along with its + traveling direction. + + The convention for positions, like a graph, is that (0,0) is the lower left corner, x increases + horizontally and y increases vertically. Therefore, north is the direction of increasing y, or (0,1). + """ + + def __init__(self, pos, direction): + self.pos = pos + self.direction = direction + + def getPosition(self): + return (self.pos) + + def getDirection(self): + return self.direction + + def isInteger(self): + x,y = self.pos + return x == int(x) and y == int(y) + + def __eq__(self, other): + if other == None: return False + return (self.pos == other.pos and self.direction == other.direction) + + def __hash__(self): + x = hash(self.pos) + y = hash(self.direction) + return hash(x + 13 * y) + + def __str__(self): + return "(x,y)="+str(self.pos)+", "+str(self.direction) + + def generateSuccessor(self, vector): + """ + Generates a new configuration reached by translating the current + configuration by the action vector. This is a low-level call and does + not attempt to respect the legality of the movement. + + Actions are movement vectors. + """ + x, y= self.pos + dx, dy = vector + direction = Actions.vectorToDirection(vector) + if direction == Directions.STOP: + direction = self.direction # There is no stop direction + return Configuration((x + dx, y+dy), direction) + +class AgentState: + """ + AgentStates hold the state of an agent (configuration, speed, scared, etc). + """ + + def __init__( self, startConfiguration, isPacman ): + self.start = startConfiguration + self.configuration = startConfiguration + self.isPacman = isPacman + self.scaredTimer = 0 + self.numCarrying = 0 + self.numReturned = 0 + + def __str__( self ): + if self.isPacman: + return "Pacman: " + str( self.configuration ) + else: + return "Ghost: " + str( self.configuration ) + + def __eq__( self, other ): + if other == None: + return False + return self.configuration == other.configuration and self.scaredTimer == other.scaredTimer + + def __hash__(self): + return hash(hash(self.configuration) + 13 * hash(self.scaredTimer)) + + def copy( self ): + state = AgentState( self.start, self.isPacman ) + state.configuration = self.configuration + state.scaredTimer = self.scaredTimer + state.numCarrying = self.numCarrying + state.numReturned = self.numReturned + return state + + def getPosition(self): + if self.configuration == None: return None + return self.configuration.getPosition() + + def getDirection(self): + return self.configuration.getDirection() + +class Grid: + """ + A 2-dimensional array of objects backed by a list of lists. Data is accessed + via grid[x][y] where (x,y) are positions on a Pacman map with x horizontal, + y vertical and the origin (0,0) in the bottom left corner. + + The __str__ method constructs an output that is oriented like a pacman board. + """ + def __init__(self, width, height, initialValue=False, bitRepresentation=None): + if initialValue not in [False, True]: raise Exception('Grids can only contain booleans') + self.CELLS_PER_INT = 30 + + self.width = width + self.height = height + self.data = [[initialValue for y in range(height)] for x in range(width)] + if bitRepresentation: + self._unpackBits(bitRepresentation) + + def __getitem__(self, i): + return self.data[i] + + def __setitem__(self, key, item): + self.data[key] = item + + def __str__(self): + out = [[str(self.data[x][y])[0] for x in range(self.width)] for y in range(self.height)] + out.reverse() + return '\n'.join([''.join(x) for x in out]) + + def __eq__(self, other): + if other == None: return False + return self.data == other.data + + def __hash__(self): + # return hash(str(self)) + base = 1 + h = 0 + for l in self.data: + for i in l: + if i: + h += base + base *= 2 + return hash(h) + + def copy(self): + g = Grid(self.width, self.height) + g.data = [x[:] for x in self.data] + return g + + def deepCopy(self): + return self.copy() + + def shallowCopy(self): + g = Grid(self.width, self.height) + g.data = self.data + return g + + def count(self, item =True ): + return sum([x.count(item) for x in self.data]) + + def asList(self, key = True): + list = [] + for x in range(self.width): + for y in range(self.height): + if self[x][y] == key: list.append( (x,y) ) + return list + + def packBits(self): + """ + Returns an efficient int list representation + + (width, height, bitPackedInts...) + """ + bits = [self.width, self.height] + currentInt = 0 + for i in range(self.height * self.width): + bit = self.CELLS_PER_INT - (i % self.CELLS_PER_INT) - 1 + x, y = self._cellIndexToPosition(i) + if self[x][y]: + currentInt += 2 ** bit + if (i + 1) % self.CELLS_PER_INT == 0: + bits.append(currentInt) + currentInt = 0 + bits.append(currentInt) + return tuple(bits) + + def _cellIndexToPosition(self, index): + x = index // self.height + y = index % self.height + return x, y + + def _unpackBits(self, bits): + """ + Fills in data from a bit-level representation + """ + cell = 0 + for packed in bits: + for bit in self._unpackInt(packed, self.CELLS_PER_INT): + if cell == self.width * self.height: break + x, y = self._cellIndexToPosition(cell) + self[x][y] = bit + cell += 1 + + def _unpackInt(self, packed, size): + bools = [] + if packed < 0: raise ValueError("must be a positive integer") + for i in range(size): + n = 2 ** (self.CELLS_PER_INT - i - 1) + if packed >= n: + bools.append(True) + packed -= n + else: + bools.append(False) + return bools + +def reconstituteGrid(bitRep): + if type(bitRep) is not type((1,2)): + return bitRep + width, height = bitRep[:2] + return Grid(width, height, bitRepresentation= bitRep[2:]) + +#################################### +# Parts you shouldn't have to read # +#################################### + +class Actions: + """ + A collection of static methods for manipulating move actions. + """ + # Directions + _directions = {Directions.NORTH: (0, 1), + Directions.SOUTH: (0, -1), + Directions.EAST: (1, 0), + Directions.WEST: (-1, 0), + Directions.STOP: (0, 0)} + + _directionsAsList = _directions.items() + + TOLERANCE = .001 + + def reverseDirection(action): + if action == Directions.NORTH: + return Directions.SOUTH + if action == Directions.SOUTH: + return Directions.NORTH + if action == Directions.EAST: + return Directions.WEST + if action == Directions.WEST: + return Directions.EAST + return action + reverseDirection = staticmethod(reverseDirection) + + def vectorToDirection(vector): + dx, dy = vector + if dy > 0: + return Directions.NORTH + if dy < 0: + return Directions.SOUTH + if dx < 0: + return Directions.WEST + if dx > 0: + return Directions.EAST + return Directions.STOP + vectorToDirection = staticmethod(vectorToDirection) + + def directionToVector(direction, speed = 1.0): + dx, dy = Actions._directions[direction] + return (dx * speed, dy * speed) + directionToVector = staticmethod(directionToVector) + + def getPossibleActions(config, walls): + possible = [] + x, y = config.pos + x_int, y_int = int(x + 0.5), int(y + 0.5) + + # In between grid points, all agents must continue straight + if (abs(x - x_int) + abs(y - y_int) > Actions.TOLERANCE): + return [config.getDirection()] + + for dir, vec in Actions._directionsAsList: + dx, dy = vec + next_y = y_int + dy + next_x = x_int + dx + if not walls[next_x][next_y]: possible.append(dir) + + return possible + + getPossibleActions = staticmethod(getPossibleActions) + + def getLegalNeighbors(position, walls): + x,y = position + x_int, y_int = int(x + 0.5), int(y + 0.5) + neighbors = [] + for dir, vec in Actions._directionsAsList: + dx, dy = vec + next_x = x_int + dx + if next_x < 0 or next_x == walls.width: continue + next_y = y_int + dy + if next_y < 0 or next_y == walls.height: continue + if not walls[next_x][next_y]: neighbors.append((next_x, next_y)) + return neighbors + getLegalNeighbors = staticmethod(getLegalNeighbors) + + def getSuccessor(position, action): + dx, dy = Actions.directionToVector(action) + x, y = position + return (x + dx, y + dy) + getSuccessor = staticmethod(getSuccessor) + +class GameStateData: + """ + + """ + def __init__( self, prevState = None ): + """ + Generates a new data packet by copying information from its predecessor. + """ + if prevState != None: + self.food = prevState.food.shallowCopy() + self.capsules = prevState.capsules[:] + self.agentStates = self.copyAgentStates( prevState.agentStates ) + self.layout = prevState.layout + self._eaten = prevState._eaten + self.score = prevState.score + + self._foodEaten = None + self._foodAdded = None + self._capsuleEaten = None + self._agentMoved = None + self._lose = False + self._win = False + self.scoreChange = 0 + + def deepCopy( self ): + state = GameStateData( self ) + state.food = self.food.deepCopy() + state.layout = self.layout.deepCopy() + state._agentMoved = self._agentMoved + state._foodEaten = self._foodEaten + state._foodAdded = self._foodAdded + state._capsuleEaten = self._capsuleEaten + return state + + def copyAgentStates( self, agentStates ): + copiedStates = [] + for agentState in agentStates: + copiedStates.append( agentState.copy() ) + return copiedStates + + def __eq__( self, other ): + """ + Allows two states to be compared. + """ + if other == None: return False + # TODO Check for type of other + if not self.agentStates == other.agentStates: return False + if not self.food == other.food: return False + if not self.capsules == other.capsules: return False + if not self.score == other.score: return False + return True + + def __hash__( self ): + """ + Allows states to be keys of dictionaries. + """ + for i, state in enumerate( self.agentStates ): + try: + int(hash(state)) + except TypeError as e: + print(e) + #hash(state) + return int((hash(tuple(self.agentStates)) + 13*hash(self.food) + 113* hash(tuple(self.capsules)) + 7 * hash(self.score)) % 1048575 ) + + def __str__( self ): + width, height = self.layout.width, self.layout.height + map = Grid(width, height) + if type(self.food) == type((1,2)): + self.food = reconstituteGrid(self.food) + for x in range(width): + for y in range(height): + food, walls = self.food, self.layout.walls + map[x][y] = self._foodWallStr(food[x][y], walls[x][y]) + + for agentState in self.agentStates: + if agentState == None: continue + if agentState.configuration == None: continue + x,y = [int( i ) for i in nearestPoint( agentState.configuration.pos )] + agent_dir = agentState.configuration.direction + if agentState.isPacman: + map[x][y] = self._pacStr( agent_dir ) + else: + map[x][y] = self._ghostStr( agent_dir ) + + for x, y in self.capsules: + map[x][y] = 'o' + + return str(map) + ("\nScore: %d\n" % self.score) + + def _foodWallStr( self, hasFood, hasWall ): + if hasFood: + return '.' + elif hasWall: + return '%' + else: + return ' ' + + def _pacStr( self, dir ): + if dir == Directions.NORTH: + return 'v' + if dir == Directions.SOUTH: + return '^' + if dir == Directions.WEST: + return '>' + return '<' + + def _ghostStr( self, dir ): + return 'G' + if dir == Directions.NORTH: + return 'M' + if dir == Directions.SOUTH: + return 'W' + if dir == Directions.WEST: + return '3' + return 'E' + + def initialize( self, layout, numGhostAgents ): + """ + Creates an initial game state from a layout array (see layout.py). + """ + self.food = layout.food.copy() + #self.capsules = [] + self.capsules = layout.capsules[:] + self.layout = layout + self.score = 0 + self.scoreChange = 0 + + self.agentStates = [] + numGhosts = 0 + for isPacman, pos in layout.agentPositions: + if not isPacman: + if numGhosts == numGhostAgents: continue # Max ghosts reached already + else: numGhosts += 1 + self.agentStates.append( AgentState( Configuration( pos, Directions.STOP), isPacman) ) + self._eaten = [False for a in self.agentStates] + +try: + import boinc + _BOINC_ENABLED = True +except: + _BOINC_ENABLED = False + +class Game: + """ + The Game manages the control flow, soliciting actions from agents. + """ + + def __init__( self, agents, display, rules, startingIndex=0, muteAgents=False, catchExceptions=False ): + self.agentCrashed = False + self.agents = agents + self.display = display + self.rules = rules + self.startingIndex = startingIndex + self.gameOver = False + self.muteAgents = muteAgents + self.catchExceptions = catchExceptions + self.moveHistory = [] + self.totalAgentTimes = [0 for agent in agents] + self.totalAgentTimeWarnings = [0 for agent in agents] + self.agentTimeout = False + import io + self.agentOutput = [io.StringIO() for agent in agents] + + def getProgress(self): + if self.gameOver: + return 1.0 + else: + return self.rules.getProgress(self) + + def _agentCrash( self, agentIndex, quiet=False): + "Helper method for handling agent crashes" + if not quiet: traceback.print_exc() + self.gameOver = True + self.agentCrashed = True + self.rules.agentCrash(self, agentIndex) + + OLD_STDOUT = None + OLD_STDERR = None + + def mute(self, agentIndex): + if not self.muteAgents: return + global OLD_STDOUT, OLD_STDERR + import io + OLD_STDOUT = sys.stdout + OLD_STDERR = sys.stderr + sys.stdout = self.agentOutput[agentIndex] + sys.stderr = self.agentOutput[agentIndex] + + def unmute(self): + if not self.muteAgents: return + global OLD_STDOUT, OLD_STDERR + # Revert stdout/stderr to originals + sys.stdout = OLD_STDOUT + sys.stderr = OLD_STDERR + + + def run( self ): + """ + Main control loop for game play. + """ + self.display.initialize(self.state.data) + self.numMoves = 0 + + ###self.display.initialize(self.state.makeObservation(1).data) + # inform learning agents of the game start + for i in range(len(self.agents)): + agent = self.agents[i] + if not agent: + self.mute(i) + # this is a null agent, meaning it failed to load + # the other team wins + print("Agent %d failed to load" % i, file=sys.stderr) + self.unmute() + self._agentCrash(i, quiet=True) + return + if ("registerInitialState" in dir(agent)): + self.mute(i) + if self.catchExceptions: + try: + timed_func = TimeoutFunction(agent.registerInitialState, int(self.rules.getMaxStartupTime(i))) + try: + start_time = time.time() + timed_func(self.state.deepCopy()) + time_taken = time.time() - start_time + self.totalAgentTimes[i] += time_taken + except TimeoutFunctionException: + print("Agent %d ran out of time on startup!" % i, file=sys.stderr) + self.unmute() + self.agentTimeout = True + self._agentCrash(i, quiet=True) + return + except Exception as data: + self._agentCrash(i, quiet=False) + self.unmute() + return + else: + agent.registerInitialState(self.state.deepCopy()) + ## TODO: could this exceed the total time + self.unmute() + + agentIndex = self.startingIndex + numAgents = len( self.agents ) + + while not self.gameOver: + # Fetch the next agent + agent = self.agents[agentIndex] + move_time = 0 + skip_action = False + # Generate an observation of the state + if 'observationFunction' in dir( agent ): + self.mute(agentIndex) + if self.catchExceptions: + try: + timed_func = TimeoutFunction(agent.observationFunction, int(self.rules.getMoveTimeout(agentIndex))) + try: + start_time = time.time() + observation = timed_func(self.state.deepCopy()) + except TimeoutFunctionException: + skip_action = True + move_time += time.time() - start_time + self.unmute() + except Exception as data: + self._agentCrash(agentIndex, quiet=False) + self.unmute() + return + else: + observation = agent.observationFunction(self.state.deepCopy()) + self.unmute() + else: + observation = self.state.deepCopy() + + # Solicit an action + action = None + self.mute(agentIndex) + if self.catchExceptions: + try: + timed_func = TimeoutFunction(agent.getAction, int(self.rules.getMoveTimeout(agentIndex)) - int(move_time)) + try: + start_time = time.time() + if skip_action: + raise TimeoutFunctionException() + action = timed_func( observation ) + except TimeoutFunctionException: + print("Agent %d timed out on a single move!" % agentIndex, file=sys.stderr) + self.agentTimeout = True + self._agentCrash(agentIndex, quiet=True) + self.unmute() + return + + move_time += time.time() - start_time + + if move_time > self.rules.getMoveWarningTime(agentIndex): + self.totalAgentTimeWarnings[agentIndex] += 1 + print("Agent %d took too long to make a move! This is warning %d" % (agentIndex, self.totalAgentTimeWarnings[agentIndex]), file=sys.stderr) + if self.totalAgentTimeWarnings[agentIndex] > self.rules.getMaxTimeWarnings(agentIndex): + print("Agent %d exceeded the maximum number of warnings: %d" % (agentIndex, self.totalAgentTimeWarnings[agentIndex]), file=sys.stderr) + self.agentTimeout = True + self._agentCrash(agentIndex, quiet=True) + self.unmute() + return + + self.totalAgentTimes[agentIndex] += move_time + #print("Agent: %d, time: %f, total: %f" % (agentIndex, move_time, self.totalAgentTimes[agentIndex])) + if self.totalAgentTimes[agentIndex] > self.rules.getMaxTotalTime(agentIndex): + print("Agent %d ran out of time! (time: %1.2f)" % (agentIndex, self.totalAgentTimes[agentIndex]), file=sys.stderr) + self.agentTimeout = True + self._agentCrash(agentIndex, quiet=True) + self.unmute() + return + self.unmute() + except Exception as data: + self._agentCrash(agentIndex) + self.unmute() + return + else: + action = agent.getAction(observation) + self.unmute() + + # Execute the action + self.moveHistory.append( (agentIndex, action) ) + if self.catchExceptions: + try: + self.state = self.state.generateSuccessor( agentIndex, action ) + except Exception as data: + self.mute(agentIndex) + self._agentCrash(agentIndex) + self.unmute() + return + else: + self.state = self.state.generateSuccessor( agentIndex, action ) + + # Change the display + self.display.update( self.state.data ) + ###idx = agentIndex - agentIndex % 2 + 1 + ###self.display.update( self.state.makeObservation(idx).data ) + + # Allow for game specific conditions (winning, losing, etc.) + self.rules.process(self.state, self) + # Track progress + if agentIndex == numAgents + 1: self.numMoves += 1 + # Next agent + agentIndex = ( agentIndex + 1 ) % numAgents + + if _BOINC_ENABLED: + boinc.set_fraction_done(self.getProgress()) + + # inform a learning agent of the game result + for agentIndex, agent in enumerate(self.agents): + if "final" in dir( agent ) : + try: + self.mute(agentIndex) + agent.final( self.state ) + self.unmute() + except Exception as data: + if not self.catchExceptions: raise data + self._agentCrash(agentIndex) + self.unmute() + return + self.display.finish() diff --git a/proj1/ghostAgents.py b/proj1/ghostAgents.py new file mode 100755 index 0000000..c3afe1f --- /dev/null +++ b/proj1/ghostAgents.py @@ -0,0 +1,81 @@ +# ghostAgents.py +# -------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +from game import Agent +from game import Actions +from game import Directions +import random +from util import manhattanDistance +import util + +class GhostAgent( Agent ): + def __init__( self, index ): + self.index = index + + def getAction( self, state ): + dist = self.getDistribution(state) + if len(dist) == 0: + return Directions.STOP + else: + return util.chooseFromDistribution( dist ) + + def getDistribution(self, state): + "Returns a Counter encoding a distribution over actions from the provided state." + util.raiseNotDefined() + +class RandomGhost( GhostAgent ): + "A ghost that chooses a legal action uniformly at random." + def getDistribution( self, state ): + dist = util.Counter() + for a in state.getLegalActions( self.index ): dist[a] = 1.0 + dist.normalize() + return dist + +class DirectionalGhost( GhostAgent ): + "A ghost that prefers to rush Pacman, or flee when scared." + def __init__( self, index, prob_attack=0.8, prob_scaredFlee=0.8 ): + self.index = index + self.prob_attack = prob_attack + self.prob_scaredFlee = prob_scaredFlee + + def getDistribution( self, state ): + # Read variables from state + ghostState = state.getGhostState( self.index ) + legalActions = state.getLegalActions( self.index ) + pos = state.getGhostPosition( self.index ) + isScared = ghostState.scaredTimer > 0 + + speed = 1 + if isScared: speed = 0.5 + + actionVectors = [Actions.directionToVector( a, speed ) for a in legalActions] + newPositions = [( pos[0]+a[0], pos[1]+a[1] ) for a in actionVectors] + pacmanPosition = state.getPacmanPosition() + + # Select best actions given the state + distancesToPacman = [manhattanDistance( pos, pacmanPosition ) for pos in newPositions] + if isScared: + bestScore = max( distancesToPacman ) + bestProb = self.prob_scaredFlee + else: + bestScore = min( distancesToPacman ) + bestProb = self.prob_attack + bestActions = [action for action, distance in zip( legalActions, distancesToPacman ) if distance == bestScore] + + # Construct distribution + dist = util.Counter() + for a in bestActions: dist[a] = bestProb / len(bestActions) + for a in legalActions: dist[a] += ( 1-bestProb ) / len(legalActions) + dist.normalize() + return dist diff --git a/proj1/grading.py b/proj1/grading.py new file mode 100755 index 0000000..5187f70 --- /dev/null +++ b/proj1/grading.py @@ -0,0 +1,325 @@ +# grading.py +# ---------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, +# Pieter Abbeel (pabbeel@cs.berkeley.edu), and Noemi Chulo. + + +"Common code for autograders" + +from html import escape +import time +import json +import traceback +from collections import defaultdict +import util + +class Grades: + "A data structure for project grades, along with formatting code to display them" + def __init__(self, projectName, questionsAndMaxesList, + gsOutput=False, edxOutput=False, muteOutput=False, questionTimeouts={}): + """ + Defines the grading scheme for a project + projectName: project name + questionsAndMaxesDict: a list of (question name, max points per question) + """ + self.questions = [el[0] for el in questionsAndMaxesList] + self.maxes = dict(questionsAndMaxesList) + self.points = Counter() + self.messages = dict([(q, []) for q in self.questions]) + self.project = projectName + self.start = time.localtime()[1:6] + self.sane = True # Sanity checks + self.currentQuestion = None # Which question we're grading + self.edxOutput = edxOutput + self.gsOutput = gsOutput # GradeScope output + self.mute = muteOutput + self.prereqs = defaultdict(set) + self.questionTimeouts = questionTimeouts + + #print('Autograder transcript for %s' % self.project) + print('Starting on %d-%d at %d:%02d:%02d' % self.start) + + def addPrereq(self, question, prereq): + self.prereqs[question].add(prereq) + + def grade(self, gradingModule, exceptionMap = {}, bonusPic = False): + """ + Grades each question + gradingModule: the module with all the grading functions (pass in with sys.modules[__name__]) + """ + + completedQuestions = set([]) + timedOutQuestions = set([]) # The set of questions for which the autograder timed out, used to print at the end + for q in self.questions: + print('\nQuestion %s' % q) + print('=' * (9 + len(q))) + print + self.currentQuestion = q + + incompleted = self.prereqs[q].difference(completedQuestions) + if len(incompleted) > 0: + prereq = incompleted.pop() + print( +"""*** NOTE: Make sure to complete Question %s before working on Question %s, +*** because Question %s builds upon your answer for Question %s. +""" % (prereq, q, q, prereq)) + continue + + if self.mute: util.mutePrint() + try: + timeout = self.questionTimeouts[q] if q in self.questionTimeouts else 1800 + util.TimeoutFunction(getattr(gradingModule, q), timeout)(self) # Call the question's function + except util.TimeoutFunctionException: + timedOutQuestions.add(q) + self.fail('Timeout: program took too long to run.') + except Exception as inst: + self.addExceptionMessage(q, inst, traceback) + self.addErrorHints(exceptionMap, inst, q[1]) + except: + self.fail('FAIL: Terminated with a string exception.') + finally: + if self.mute: util.unmutePrint() + + if self.points[q] >= self.maxes[q]: + completedQuestions.add(q) + + print('\n### Question %s: %d/%d ###\n' % (q, self.points[q], self.maxes[q])) + + + print('\nFinished at %d:%02d:%02d' % time.localtime()[3:6]) + print("\nProvisional grades\n==================") + + for q in self.questions: + print('Question %s: %d/%d %s' % (q, self.points[q], self.maxes[q], "program timed out" if q in timedOutQuestions else "")) + print('------------------') + print('Total: %d/%d' % (self.points.totalCount(), sum(self.maxes.values()))) + if bonusPic and self.points.totalCount() == 25: + print(""" + + ALL HAIL GRANDPAC. + LONG LIVE THE GHOSTBUSTING KING. + + --- ---- --- + | \ / + \ / | + | + \--/ \--/ + | + | + + | + | + + + | + @@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + \ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + \ / @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + V \ @@@@@@@@@@@@@@@@@@@@@@@@@@@@ + \ / @@@@@@@@@@@@@@@@@@@@@@@@@@ + V @@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@ + /\ @@@@@@@@@@@@@@@@@@@@@@ + / \ @@@@@@@@@@@@@@@@@@@@@@@@@ + /\ / @@@@@@@@@@@@@@@@@@@@@@@@@@@ + / \ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + / @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@@ + +""") + print(""" +Your grades are NOT yet registered. To register your grades, make sure +to follow your instructor's guidelines to receive credit on your project. +""") + + if self.edxOutput: + self.produceOutput() + if self.gsOutput: + self.produceGradeScopeOutput() + + def addExceptionMessage(self, q, inst, traceback): + """ + Method to format the exception message, this is more complicated because + we need to escape the traceback but wrap the exception in a
 tag
+    """
+    self.fail('FAIL: Exception raised: %s' % inst)
+    self.addMessage('')
+    for line in traceback.format_exc().split('\n'):
+        self.addMessage(line)
+
+  def addErrorHints(self, exceptionMap, errorInstance, questionNum):
+    typeOf = str(type(errorInstance))
+    questionName = 'q' + questionNum
+    errorHint = ''
+
+    # question specific error hints
+    if exceptionMap.get(questionName):
+      questionMap = exceptionMap.get(questionName)
+      if (questionMap.get(typeOf)):
+        errorHint = questionMap.get(typeOf)
+    # fall back to general error messages if a question specific
+    # one does not exist
+    if (exceptionMap.get(typeOf)):
+      errorHint = exceptionMap.get(typeOf)
+
+    # dont include the HTML if we have no error hint
+    if not errorHint:
+      return ''
+
+    for line in errorHint.split('\n'):
+      self.addMessage(line)
+
+  def produceGradeScopeOutput(self):
+    out_dct = {}
+
+    # total of entire submission
+    total_possible = sum(self.maxes.values())
+    total_score = sum(self.points.values())
+    out_dct['score'] = total_score
+    out_dct['max_score'] = total_possible
+    out_dct['output'] = "Total score (%d / %d)" % (total_score, total_possible)
+
+    # individual tests
+    tests_out = []
+    for name in self.questions:
+      test_out = {}
+      # test name
+      test_out['name'] = name
+      # test score
+      test_out['score'] = self.points[name]
+      test_out['max_score'] = self.maxes[name]
+      # others
+      is_correct = self.points[name] >= self.maxes[name]
+      test_out['output'] = "  Question {num} ({points}/{max}) {correct}".format(
+          num=(name[1] if len(name) == 2 else name),
+          points=test_out['score'],
+          max=test_out['max_score'],
+          correct=('X' if not is_correct else ''),
+      )
+      test_out['tags'] = []
+      tests_out.append(test_out)
+    out_dct['tests'] = tests_out
+
+    # file output
+    with open('gradescope_response.json', 'w') as outfile:
+        json.dump(out_dct, outfile)
+    return
+
+  def produceOutput(self):
+    edxOutput = open('edx_response.html', 'w')
+    edxOutput.write("
") + + # first sum + total_possible = sum(self.maxes.values()) + total_score = sum(self.points.values()) + checkOrX = '' + if (total_score >= total_possible): + checkOrX = '' + header = """ +

+ Total score ({total_score} / {total_possible}) +

+ """.format(total_score = total_score, + total_possible = total_possible, + checkOrX = checkOrX + ) + edxOutput.write(header) + + for q in self.questions: + if len(q) == 2: + name = q[1] + else: + name = q + checkOrX = '' + if (self.points[q] >= self.maxes[q]): + checkOrX = '' + #messages = '\n
\n'.join(self.messages[q]) + messages = "
%s
" % '\n'.join(self.messages[q]) + output = """ +
+
+
+ Question {q} ({points}/{max}) {checkOrX} +
+
+ {messages} +
+
+
+ """.format(q = name, + max = self.maxes[q], + messages = messages, + checkOrX = checkOrX, + points = self.points[q] + ) + # print("*** output for Question %s " % q[1]) + # print(output) + edxOutput.write(output) + edxOutput.write("
") + edxOutput.close() + edxOutput = open('edx_grade', 'w') + edxOutput.write(str(self.points.totalCount())) + edxOutput.close() + + def fail(self, message, raw=False): + "Sets sanity check bit to false and outputs a message" + self.sane = False + self.assignZeroCredit() + self.addMessage(message, raw) + + def assignZeroCredit(self): + self.points[self.currentQuestion] = 0 + + def addPoints(self, amt): + self.points[self.currentQuestion] += amt + + def deductPoints(self, amt): + self.points[self.currentQuestion] -= amt + + def assignFullCredit(self, message="", raw=False): + self.points[self.currentQuestion] = self.maxes[self.currentQuestion] + if message != "": + self.addMessage(message, raw) + + def addMessage(self, message, raw=False): + if not raw: + # We assume raw messages, formatted for HTML, are printed separately + if self.mute: util.unmutePrint() + print('*** ' + message) + if self.mute: util.mutePrint() + message = escape(message) + self.messages[self.currentQuestion].append(message) + + def addMessageToEmail(self, message): + print("WARNING**** addMessageToEmail is deprecated %s" % message) + for line in message.split('\n'): + pass + #print('%%% ' + line + ' %%%') + #self.messages[self.currentQuestion].append(line) + + + + + +class Counter(dict): + """ + Dict with default 0 + """ + def __getitem__(self, idx): + try: + return dict.__getitem__(self, idx) + except KeyError: + return 0 + + def totalCount(self): + """ + Returns the sum of counts for all keys. + """ + return sum(self.values()) diff --git a/proj1/graphicsDisplay.py b/proj1/graphicsDisplay.py new file mode 100755 index 0000000..bd53e9a --- /dev/null +++ b/proj1/graphicsDisplay.py @@ -0,0 +1,679 @@ +# graphicsDisplay.py +# ------------------ +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +from graphicsUtils import * +import math, time +from game import Directions + +########################### +# GRAPHICS DISPLAY CODE # +########################### + +# Most code by Dan Klein and John Denero written or rewritten for cs188, UC Berkeley. +# Some code from a Pacman implementation by LiveWires, and used / modified with permission. + +DEFAULT_GRID_SIZE = 30.0 +INFO_PANE_HEIGHT = 35 +BACKGROUND_COLOR = formatColor(0,0,0) +WALL_COLOR = formatColor(0.0/255.0, 51.0/255.0, 255.0/255.0) +INFO_PANE_COLOR = formatColor(.4,.4,0) +SCORE_COLOR = formatColor(.9, .9, .9) +PACMAN_OUTLINE_WIDTH = 2 +PACMAN_CAPTURE_OUTLINE_WIDTH = 4 + +GHOST_COLORS = [] +GHOST_COLORS.append(formatColor(.9,0,0)) # Red +GHOST_COLORS.append(formatColor(0,.3,.9)) # Blue +GHOST_COLORS.append(formatColor(.98,.41,.07)) # Orange +GHOST_COLORS.append(formatColor(.1,.75,.7)) # Green +GHOST_COLORS.append(formatColor(1.0,0.6,0.0)) # Yellow +GHOST_COLORS.append(formatColor(.4,0.13,0.91)) # Purple + +TEAM_COLORS = GHOST_COLORS[:2] + +GHOST_SHAPE = [ + ( 0, 0.3 ), + ( 0.25, 0.75 ), + ( 0.5, 0.3 ), + ( 0.75, 0.75 ), + ( 0.75, -0.5 ), + ( 0.5, -0.75 ), + (-0.5, -0.75 ), + (-0.75, -0.5 ), + (-0.75, 0.75 ), + (-0.5, 0.3 ), + (-0.25, 0.75 ) + ] +GHOST_SIZE = 0.65 +SCARED_COLOR = formatColor(1,1,1) + +GHOST_VEC_COLORS = [colorToVector(c) for c in GHOST_COLORS] + +PACMAN_COLOR = formatColor(255.0/255.0,255.0/255.0,61.0/255) +PACMAN_SCALE = 0.5 +#pacman_speed = 0.25 + +# Food +FOOD_COLOR = formatColor(1,1,1) +FOOD_SIZE = 0.1 + +# Laser +LASER_COLOR = formatColor(1,0,0) +LASER_SIZE = 0.02 + +# Capsule graphics +CAPSULE_COLOR = formatColor(1,1,1) +CAPSULE_SIZE = 0.25 + +# Drawing walls +WALL_RADIUS = 0.15 + +class InfoPane: + def __init__(self, layout, gridSize): + self.gridSize = gridSize + self.width = (layout.width) * gridSize + self.base = (layout.height + 1) * gridSize + self.height = INFO_PANE_HEIGHT + self.fontSize = 24 + self.textColor = PACMAN_COLOR + self.drawPane() + + def toScreen(self, pos, y = None): + """ + Translates a point relative from the bottom left of the info pane. + """ + if y == None: + x,y = pos + else: + x = pos + + x = self.gridSize + x # Margin + y = self.base + y + return x,y + + def drawPane(self): + self.scoreText = text( self.toScreen(0, 0 ), self.textColor, "SCORE: 0", "Times", self.fontSize, "bold") + + def initializeGhostDistances(self, distances): + self.ghostDistanceText = [] + + size = 20 + if self.width < 240: + size = 12 + if self.width < 160: + size = 10 + + for i, d in enumerate(distances): + t = text( self.toScreen(self.width//2 + self.width//8 * i, 0), GHOST_COLORS[i+1], d, "Times", size, "bold") + self.ghostDistanceText.append(t) + + def updateScore(self, score): + changeText(self.scoreText, "SCORE: % 4d" % score) + + def setTeam(self, isBlue): + text = "RED TEAM" + if isBlue: text = "BLUE TEAM" + self.teamText = text( self.toScreen(300, 0 ), self.textColor, text, "Times", self.fontSize, "bold") + + def updateGhostDistances(self, distances): + if len(distances) == 0: return + if 'ghostDistanceText' not in dir(self): self.initializeGhostDistances(distances) + else: + for i, d in enumerate(distances): + changeText(self.ghostDistanceText[i], d) + + def drawGhost(self): + pass + + def drawPacman(self): + pass + + def drawWarning(self): + pass + + def clearIcon(self): + pass + + def updateMessage(self, message): + pass + + def clearMessage(self): + pass + + +class PacmanGraphics: + def __init__(self, zoom=1.0, frameTime=0.0, capture=False): + self.have_window = 0 + self.currentGhostImages = {} + self.pacmanImage = None + self.zoom = zoom + self.gridSize = DEFAULT_GRID_SIZE * zoom + self.capture = capture + self.frameTime = frameTime + + def checkNullDisplay(self): + return False + + def initialize(self, state, isBlue = False): + self.isBlue = isBlue + self.startGraphics(state) + + # self.drawDistributions(state) + self.distributionImages = None # Initialized lazily + self.drawStaticObjects(state) + self.drawAgentObjects(state) + + # Information + self.previousState = state + + def startGraphics(self, state): + self.layout = state.layout + layout = self.layout + self.width = layout.width + self.height = layout.height + self.make_window(self.width, self.height) + self.infoPane = InfoPane(layout, self.gridSize) + self.currentState = layout + + def drawDistributions(self, state): + walls = state.layout.walls + dist = [] + for x in range(walls.width): + distx = [] + dist.append(distx) + for y in range(walls.height): + ( screen_x, screen_y ) = self.to_screen( (x, y) ) + block = square( (screen_x, screen_y), + 0.5 * self.gridSize, + color = BACKGROUND_COLOR, + filled = 1, behind=2) + distx.append(block) + self.distributionImages = dist + + def drawStaticObjects(self, state): + layout = self.layout + self.drawWalls(layout.walls) + self.food = self.drawFood(layout.food) + self.capsules = self.drawCapsules(layout.capsules) + refresh() + + def drawAgentObjects(self, state): + self.agentImages = [] # (agentState, image) + for index, agent in enumerate(state.agentStates): + if agent.isPacman: + image = self.drawPacman(agent, index) + self.agentImages.append( (agent, image) ) + else: + image = self.drawGhost(agent, index) + self.agentImages.append( (agent, image) ) + refresh() + + def swapImages(self, agentIndex, newState): + """ + Changes an image from a ghost to a pacman or vis versa (for capture) + """ + prevState, prevImage = self.agentImages[agentIndex] + for item in prevImage: remove_from_screen(item) + if newState.isPacman: + image = self.drawPacman(newState, agentIndex) + self.agentImages[agentIndex] = (newState, image ) + else: + image = self.drawGhost(newState, agentIndex) + self.agentImages[agentIndex] = (newState, image ) + refresh() + + def update(self, newState): + agentIndex = newState._agentMoved + agentState = newState.agentStates[agentIndex] + + if self.agentImages[agentIndex][0].isPacman != agentState.isPacman: self.swapImages(agentIndex, agentState) + prevState, prevImage = self.agentImages[agentIndex] + if agentState.isPacman: + self.animatePacman(agentState, prevState, prevImage) + else: + self.moveGhost(agentState, agentIndex, prevState, prevImage) + self.agentImages[agentIndex] = (agentState, prevImage) + + if newState._foodEaten != None: + self.removeFood(newState._foodEaten, self.food) + if newState._capsuleEaten != None: + self.removeCapsule(newState._capsuleEaten, self.capsules) + self.infoPane.updateScore(newState.score) + if 'ghostDistances' in dir(newState): + self.infoPane.updateGhostDistances(newState.ghostDistances) + + def make_window(self, width, height): + grid_width = (width-1) * self.gridSize + grid_height = (height-1) * self.gridSize + screen_width = 2*self.gridSize + grid_width + screen_height = 2*self.gridSize + grid_height + INFO_PANE_HEIGHT + + begin_graphics(screen_width, + screen_height, + BACKGROUND_COLOR, + "CS188 Pacman") + + def drawPacman(self, pacman, index): + position = self.getPosition(pacman) + screen_point = self.to_screen(position) + endpoints = self.getEndpoints(self.getDirection(pacman)) + + width = PACMAN_OUTLINE_WIDTH + outlineColor = PACMAN_COLOR + fillColor = PACMAN_COLOR + + if self.capture: + outlineColor = TEAM_COLORS[index % 2] + fillColor = GHOST_COLORS[index] + width = PACMAN_CAPTURE_OUTLINE_WIDTH + + return [circle(screen_point, PACMAN_SCALE * self.gridSize, + fillColor = fillColor, outlineColor = outlineColor, + endpoints = endpoints, + width = width)] + + def getEndpoints(self, direction, position=(0,0)): + x, y = position + pos = x - int(x) + y - int(y) + width = 30 + 80 * math.sin(math.pi* pos) + + delta = width / 2 + if (direction == 'West'): + endpoints = (180+delta, 180-delta) + elif (direction == 'North'): + endpoints = (90+delta, 90-delta) + elif (direction == 'South'): + endpoints = (270+delta, 270-delta) + else: + endpoints = (0+delta, 0-delta) + return endpoints + + def movePacman(self, position, direction, image): + screenPosition = self.to_screen(position) + endpoints = self.getEndpoints( direction, position ) + r = PACMAN_SCALE * self.gridSize + moveCircle(image[0], screenPosition, r, endpoints) + refresh() + + def animatePacman(self, pacman, prevPacman, image): + if self.frameTime < 0: + print('Press any key to step forward, "q" to play') + keys = wait_for_keys() + if 'q' in keys: + self.frameTime = 0.1 + if self.frameTime > 0.01 or self.frameTime < 0: + start = time.time() + fx, fy = self.getPosition(prevPacman) + px, py = self.getPosition(pacman) + frames = 4.0 + for i in range(1,int(frames) + 1): + pos = px*i/frames + fx*(frames-i)/frames, py*i/frames + fy*(frames-i)/frames + self.movePacman(pos, self.getDirection(pacman), image) + refresh() + sleep(abs(self.frameTime) / frames) + else: + self.movePacman(self.getPosition(pacman), self.getDirection(pacman), image) + refresh() + + def getGhostColor(self, ghost, ghostIndex): + if ghost.scaredTimer > 0: + return SCARED_COLOR + else: + return GHOST_COLORS[ghostIndex] + + def drawGhost(self, ghost, agentIndex): + pos = self.getPosition(ghost) + dir = self.getDirection(ghost) + (screen_x, screen_y) = (self.to_screen(pos) ) + coords = [] + for (x, y) in GHOST_SHAPE: + coords.append((x*self.gridSize*GHOST_SIZE + screen_x, y*self.gridSize*GHOST_SIZE + screen_y)) + + colour = self.getGhostColor(ghost, agentIndex) + body = polygon(coords, colour, filled = 1) + WHITE = formatColor(1.0, 1.0, 1.0) + BLACK = formatColor(0.0, 0.0, 0.0) + + dx = 0 + dy = 0 + if dir == 'North': + dy = -0.2 + if dir == 'South': + dy = 0.2 + if dir == 'East': + dx = 0.2 + if dir == 'West': + dx = -0.2 + leftEye = circle((screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2, WHITE, WHITE) + rightEye = circle((screen_x+self.gridSize*GHOST_SIZE*(0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2, WHITE, WHITE) + leftPupil = circle((screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08, BLACK, BLACK) + rightPupil = circle((screen_x+self.gridSize*GHOST_SIZE*(0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08, BLACK, BLACK) + ghostImageParts = [] + ghostImageParts.append(body) + ghostImageParts.append(leftEye) + ghostImageParts.append(rightEye) + ghostImageParts.append(leftPupil) + ghostImageParts.append(rightPupil) + + return ghostImageParts + + def moveEyes(self, pos, dir, eyes): + (screen_x, screen_y) = (self.to_screen(pos) ) + dx = 0 + dy = 0 + if dir == 'North': + dy = -0.2 + if dir == 'South': + dy = 0.2 + if dir == 'East': + dx = 0.2 + if dir == 'West': + dx = -0.2 + moveCircle(eyes[0],(screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2) + moveCircle(eyes[1],(screen_x+self.gridSize*GHOST_SIZE*(0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2) + moveCircle(eyes[2],(screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08) + moveCircle(eyes[3],(screen_x+self.gridSize*GHOST_SIZE*(0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08) + + def moveGhost(self, ghost, ghostIndex, prevGhost, ghostImageParts): + old_x, old_y = self.to_screen(self.getPosition(prevGhost)) + new_x, new_y = self.to_screen(self.getPosition(ghost)) + delta = new_x - old_x, new_y - old_y + + for ghostImagePart in ghostImageParts: + move_by(ghostImagePart, delta) + refresh() + + if ghost.scaredTimer > 0: + color = SCARED_COLOR + else: + color = GHOST_COLORS[ghostIndex] + edit(ghostImageParts[0], ('fill', color), ('outline', color)) + self.moveEyes(self.getPosition(ghost), self.getDirection(ghost), ghostImageParts[-4:]) + refresh() + + def getPosition(self, agentState): + if agentState.configuration == None: return (-1000, -1000) + return agentState.getPosition() + + def getDirection(self, agentState): + if agentState.configuration == None: return Directions.STOP + return agentState.configuration.getDirection() + + def finish(self): + end_graphics() + + def to_screen(self, point): + ( x, y ) = point + #y = self.height - y + x = (x + 1)*self.gridSize + y = (self.height - y)*self.gridSize + return ( x, y ) + + # Fixes some TK issue with off-center circles + def to_screen2(self, point): + ( x, y ) = point + #y = self.height - y + x = (x + 1)*self.gridSize + y = (self.height - y)*self.gridSize + return ( x, y ) + + def drawWalls(self, wallMatrix): + wallColor = WALL_COLOR + for xNum, x in enumerate(wallMatrix): + if self.capture and (xNum * 2) < wallMatrix.width: wallColor = TEAM_COLORS[0] + if self.capture and (xNum * 2) >= wallMatrix.width: wallColor = TEAM_COLORS[1] + + for yNum, cell in enumerate(x): + if cell: # There's a wall here + pos = (xNum, yNum) + screen = self.to_screen(pos) + screen2 = self.to_screen2(pos) + + # draw each quadrant of the square based on adjacent walls + wIsWall = self.isWall(xNum-1, yNum, wallMatrix) + eIsWall = self.isWall(xNum+1, yNum, wallMatrix) + nIsWall = self.isWall(xNum, yNum+1, wallMatrix) + sIsWall = self.isWall(xNum, yNum-1, wallMatrix) + nwIsWall = self.isWall(xNum-1, yNum+1, wallMatrix) + swIsWall = self.isWall(xNum-1, yNum-1, wallMatrix) + neIsWall = self.isWall(xNum+1, yNum+1, wallMatrix) + seIsWall = self.isWall(xNum+1, yNum-1, wallMatrix) + + # NE quadrant + if (not nIsWall) and (not eIsWall): + # inner circle + circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (0,91), 'arc') + if (nIsWall) and (not eIsWall): + # vertical line + line(add(screen, (self.gridSize*WALL_RADIUS, 0)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-0.5)-1)), wallColor) + if (not nIsWall) and (eIsWall): + # horizontal line + line(add(screen, (0, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5+1, self.gridSize*(-1)*WALL_RADIUS)), wallColor) + if (nIsWall) and (eIsWall) and (not neIsWall): + # outer circle + circle(add(screen2, (self.gridSize*2*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (180,271), 'arc') + line(add(screen, (self.gridSize*2*WALL_RADIUS-1, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5+1, self.gridSize*(-1)*WALL_RADIUS)), wallColor) + line(add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS+1)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-0.5))), wallColor) + + # NW quadrant + if (not nIsWall) and (not wIsWall): + # inner circle + circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (90,181), 'arc') + if (nIsWall) and (not wIsWall): + # vertical line + line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, 0)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-0.5)-1)), wallColor) + if (not nIsWall) and (wIsWall): + # horizontal line + line(add(screen, (0, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5)-1, self.gridSize*(-1)*WALL_RADIUS)), wallColor) + if (nIsWall) and (wIsWall) and (not nwIsWall): + # outer circle + circle(add(screen2, (self.gridSize*(-2)*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (270,361), 'arc') + line(add(screen, (self.gridSize*(-2)*WALL_RADIUS+1, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5), self.gridSize*(-1)*WALL_RADIUS)), wallColor) + line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS+1)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-0.5))), wallColor) + + # SE quadrant + if (not sIsWall) and (not eIsWall): + # inner circle + circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (270,361), 'arc') + if (sIsWall) and (not eIsWall): + # vertical line + line(add(screen, (self.gridSize*WALL_RADIUS, 0)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(0.5)+1)), wallColor) + if (not sIsWall) and (eIsWall): + # horizontal line + line(add(screen, (0, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5+1, self.gridSize*(1)*WALL_RADIUS)), wallColor) + if (sIsWall) and (eIsWall) and (not seIsWall): + # outer circle + circle(add(screen2, (self.gridSize*2*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (90,181), 'arc') + line(add(screen, (self.gridSize*2*WALL_RADIUS-1, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5, self.gridSize*(1)*WALL_RADIUS)), wallColor) + line(add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS-1)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(0.5))), wallColor) + + # SW quadrant + if (not sIsWall) and (not wIsWall): + # inner circle + circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (180,271), 'arc') + if (sIsWall) and (not wIsWall): + # vertical line + line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, 0)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(0.5)+1)), wallColor) + if (not sIsWall) and (wIsWall): + # horizontal line + line(add(screen, (0, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5)-1, self.gridSize*(1)*WALL_RADIUS)), wallColor) + if (sIsWall) and (wIsWall) and (not swIsWall): + # outer circle + circle(add(screen2, (self.gridSize*(-2)*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (0,91), 'arc') + line(add(screen, (self.gridSize*(-2)*WALL_RADIUS+1, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5), self.gridSize*(1)*WALL_RADIUS)), wallColor) + line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS-1)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(0.5))), wallColor) + + def isWall(self, x, y, walls): + if x < 0 or y < 0: + return False + if x >= walls.width or y >= walls.height: + return False + return walls[x][y] + + def drawFood(self, foodMatrix ): + foodImages = [] + color = FOOD_COLOR + for xNum, x in enumerate(foodMatrix): + if self.capture and (xNum * 2) <= foodMatrix.width: color = TEAM_COLORS[0] + if self.capture and (xNum * 2) > foodMatrix.width: color = TEAM_COLORS[1] + imageRow = [] + foodImages.append(imageRow) + for yNum, cell in enumerate(x): + if cell: # There's food here + screen = self.to_screen((xNum, yNum )) + dot = circle( screen, + FOOD_SIZE * self.gridSize, + outlineColor = color, fillColor = color, + width = 1) + imageRow.append(dot) + else: + imageRow.append(None) + return foodImages + + def drawCapsules(self, capsules ): + capsuleImages = {} + for capsule in capsules: + ( screen_x, screen_y ) = self.to_screen(capsule) + dot = circle( (screen_x, screen_y), + CAPSULE_SIZE * self.gridSize, + outlineColor = CAPSULE_COLOR, + fillColor = CAPSULE_COLOR, + width = 1) + capsuleImages[capsule] = dot + return capsuleImages + + def removeFood(self, cell, foodImages ): + x, y = cell + remove_from_screen(foodImages[x][y]) + + def removeCapsule(self, cell, capsuleImages ): + x, y = cell + remove_from_screen(capsuleImages[(x, y)]) + + def drawExpandedCells(self, cells): + """ + Draws an overlay of expanded grid positions for search agents + """ + n = float(len(cells)) + baseColor = [1.0, 0.0, 0.0] + self.clearExpandedCells() + self.expandedCells = [] + for k, cell in enumerate(cells): + screenPos = self.to_screen( cell) + cellColor = formatColor(*[(n-k) * c * .5 / n + .25 for c in baseColor]) + block = square(screenPos, + 0.5 * self.gridSize, + color = cellColor, + filled = 1, behind=2) + self.expandedCells.append(block) + if self.frameTime < 0: + refresh() + + def clearExpandedCells(self): + if 'expandedCells' in dir(self) and len(self.expandedCells) > 0: + for cell in self.expandedCells: + remove_from_screen(cell) + + + def updateDistributions(self, distributions): + "Draws an agent's belief distributions" + # copy all distributions so we don't change their state + distributions = map(lambda x: x.copy(), distributions) + if self.distributionImages == None: + self.drawDistributions(self.previousState) + for x in range(len(self.distributionImages)): + for y in range(len(self.distributionImages[0])): + image = self.distributionImages[x][y] + weights = [dist[ (x,y) ] for dist in distributions] + + if sum(weights) != 0: + pass + # Fog of war + color = [0.0,0.0,0.0] + colors = GHOST_VEC_COLORS[1:] # With Pacman + if self.capture: colors = GHOST_VEC_COLORS + for weight, gcolor in zip(weights, colors): + color = [min(1.0, c + 0.95 * g * weight ** .3) for c,g in zip(color, gcolor)] + changeColor(image, formatColor(*color)) + refresh() + +class FirstPersonPacmanGraphics(PacmanGraphics): + def __init__(self, zoom = 1.0, showGhosts = True, capture = False, frameTime=0): + PacmanGraphics.__init__(self, zoom, frameTime=frameTime) + self.showGhosts = showGhosts + self.capture = capture + + def initialize(self, state, isBlue = False): + + self.isBlue = isBlue + PacmanGraphics.startGraphics(self, state) + # Initialize distribution images + walls = state.layout.walls + dist = [] + self.layout = state.layout + + # Draw the rest + self.distributionImages = None # initialize lazily + self.drawStaticObjects(state) + self.drawAgentObjects(state) + + # Information + self.previousState = state + + def lookAhead(self, config, state): + if config.getDirection() == 'Stop': + return + else: + pass + # Draw relevant ghosts + allGhosts = state.getGhostStates() + visibleGhosts = state.getVisibleGhosts() + for i, ghost in enumerate(allGhosts): + if ghost in visibleGhosts: + self.drawGhost(ghost, i) + else: + self.currentGhostImages[i] = None + + def getGhostColor(self, ghost, ghostIndex): + return GHOST_COLORS[ghostIndex] + + def getPosition(self, ghostState): + if not self.showGhosts and not ghostState.isPacman and ghostState.getPosition()[1] > 1: + return (-1000, -1000) + else: + return PacmanGraphics.getPosition(self, ghostState) + +def add(x, y): + return (x[0] + y[0], x[1] + y[1]) + + +# Saving graphical output +# ----------------------- +# Note: to make an animated gif from this postscript output, try the command: +# convert -delay 7 -loop 1 -compress lzw -layers optimize frame* out.gif +# convert is part of imagemagick (freeware) + +SAVE_POSTSCRIPT = False +POSTSCRIPT_OUTPUT_DIR = 'frames' +FRAME_NUMBER = 0 +import os + +def saveFrame(): + "Saves the current graphical output as a postscript file" + global SAVE_POSTSCRIPT, FRAME_NUMBER, POSTSCRIPT_OUTPUT_DIR + if not SAVE_POSTSCRIPT: return + if not os.path.exists(POSTSCRIPT_OUTPUT_DIR): os.mkdir(POSTSCRIPT_OUTPUT_DIR) + name = os.path.join(POSTSCRIPT_OUTPUT_DIR, 'frame_%08d.ps' % FRAME_NUMBER) + FRAME_NUMBER += 1 + writePostscript(name) # writes the current canvas diff --git a/proj1/graphicsUtils.py b/proj1/graphicsUtils.py new file mode 100755 index 0000000..22e6ae1 --- /dev/null +++ b/proj1/graphicsUtils.py @@ -0,0 +1,402 @@ +# graphicsUtils.py +# ---------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +import sys +import math +import random +import string +import time +import types +import tkinter +import os.path + +_Windows = sys.platform == 'win32' # True if on Win95/98/NT + +_root_window = None # The root window for graphics output +_canvas = None # The canvas which holds graphics +_canvas_xs = None # Size of canvas object +_canvas_ys = None +_canvas_x = None # Current position on canvas +_canvas_y = None +_canvas_col = None # Current colour (set to black below) +_canvas_tsize = 12 +_canvas_tserifs = 0 + +def formatColor(r, g, b): + return '#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255)) + +def colorToVector(color): + return list(map(lambda x: int(x, 16) / 256.0, [color[1:3], color[3:5], color[5:7]])) + +if _Windows: + _canvas_tfonts = ['times new roman', 'lucida console'] +else: + _canvas_tfonts = ['times', 'lucidasans-24'] + pass # XXX need defaults here + +def sleep(secs): + global _root_window + if _root_window == None: + time.sleep(secs) + else: + _root_window.update_idletasks() + _root_window.after(int(1000 * secs), _root_window.quit) + _root_window.mainloop() + +def begin_graphics(width=640, height=480, color=formatColor(0, 0, 0), title=None): + + global _root_window, _canvas, _canvas_x, _canvas_y, _canvas_xs, _canvas_ys, _bg_color + + # Check for duplicate call + if _root_window is not None: + # Lose the window. + _root_window.destroy() + + # Save the canvas size parameters + _canvas_xs, _canvas_ys = width - 1, height - 1 + _canvas_x, _canvas_y = 0, _canvas_ys + _bg_color = color + + # Create the root window + _root_window = tkinter.Tk() + _root_window.protocol('WM_DELETE_WINDOW', _destroy_window) + _root_window.title(title or 'Graphics Window') + _root_window.resizable(0, 0) + + # Create the canvas object + try: + _canvas = tkinter.Canvas(_root_window, width=width, height=height) + _canvas.pack() + draw_background() + _canvas.update() + except: + _root_window = None + raise + + # Bind to key-down and key-up events + _root_window.bind( "", _keypress ) + _root_window.bind( "", _keyrelease ) + _root_window.bind( "", _clear_keys ) + _root_window.bind( "", _clear_keys ) + _root_window.bind( "", _leftclick ) + _root_window.bind( "", _rightclick ) + _root_window.bind( "", _rightclick ) + _root_window.bind( "", _ctrl_leftclick) + _clear_keys() + +_leftclick_loc = None +_rightclick_loc = None +_ctrl_leftclick_loc = None + +def _leftclick(event): + global _leftclick_loc + _leftclick_loc = (event.x, event.y) + +def _rightclick(event): + global _rightclick_loc + _rightclick_loc = (event.x, event.y) + +def _ctrl_leftclick(event): + global _ctrl_leftclick_loc + _ctrl_leftclick_loc = (event.x, event.y) + +def wait_for_click(): + while True: + global _leftclick_loc + global _rightclick_loc + global _ctrl_leftclick_loc + if _leftclick_loc != None: + val = _leftclick_loc + _leftclick_loc = None + return val, 'left' + if _rightclick_loc != None: + val = _rightclick_loc + _rightclick_loc = None + return val, 'right' + if _ctrl_leftclick_loc != None: + val = _ctrl_leftclick_loc + _ctrl_leftclick_loc = None + return val, 'ctrl_left' + sleep(0.05) + +def draw_background(): + corners = [(0,0), (0, _canvas_ys), (_canvas_xs, _canvas_ys), (_canvas_xs, 0)] + polygon(corners, _bg_color, fillColor=_bg_color, filled=True, smoothed=False) + +def _destroy_window(event=None): + sys.exit(0) +# global _root_window +# _root_window.destroy() +# _root_window = None + #print("DESTROY") + +def end_graphics(): + global _root_window, _canvas, _mouse_enabled + try: + try: + sleep(1) + if _root_window != None: + _root_window.destroy() + except SystemExit as e: + print('Ending graphics raised an exception:', e) + finally: + _root_window = None + _canvas = None + _mouse_enabled = 0 + _clear_keys() + +def clear_screen(background=None): + global _canvas_x, _canvas_y + _canvas.delete('all') + draw_background() + _canvas_x, _canvas_y = 0, _canvas_ys + +def polygon(coords, outlineColor, fillColor=None, filled=1, smoothed=1, behind=0, width=1): + c = [] + for coord in coords: + c.append(coord[0]) + c.append(coord[1]) + if fillColor == None: fillColor = outlineColor + if filled == 0: fillColor = "" + poly = _canvas.create_polygon(c, outline=outlineColor, fill=fillColor, smooth=smoothed, width=width) + if behind > 0: + _canvas.tag_lower(poly, behind) # Higher should be more visible + return poly + +def square(pos, r, color, filled=1, behind=0): + x, y = pos + coords = [(x - r, y - r), (x + r, y - r), (x + r, y + r), (x - r, y + r)] + return polygon(coords, color, color, filled, 0, behind=behind) + +def circle(pos, r, outlineColor, fillColor=None, endpoints=None, style='pieslice', width=2): + x, y = pos + x0, x1 = x - r - 1, x + r + y0, y1 = y - r - 1, y + r + if endpoints == None: + e = [0, 359] + else: + e = list(endpoints) + while e[0] > e[1]: e[1] = e[1] + 360 + + return _canvas.create_arc(x0, y0, x1, y1, outline=outlineColor, fill=fillColor or outlineColor, + extent=e[1] - e[0], start=e[0], style=style, width=width) + +def image(pos, file="../../blueghost.gif"): + x, y = pos + # img = PhotoImage(file=file) + return _canvas.create_image(x, y, image = tkinter.PhotoImage(file=file), anchor = tkinter.NW) + + +def refresh(): + _canvas.update_idletasks() + +def moveCircle(id, pos, r, endpoints=None): + global _canvas_x, _canvas_y + + x, y = pos +# x0, x1 = x - r, x + r + 1 +# y0, y1 = y - r, y + r + 1 + x0, x1 = x - r - 1, x + r + y0, y1 = y - r - 1, y + r + if endpoints == None: + e = [0, 359] + else: + e = list(endpoints) + while e[0] > e[1]: e[1] = e[1] + 360 + + if os.path.isfile('flag'): + edit(id, ('extent', e[1] - e[0])) + else: + edit(id, ('start', e[0]), ('extent', e[1] - e[0])) + move_to(id, x0, y0) + +def edit(id, *args): + _canvas.itemconfigure(id, **dict(args)) + +def text(pos, color, contents, font='Helvetica', size=12, style='normal', anchor="nw"): + global _canvas_x, _canvas_y + x, y = pos + font = (font, str(size), style) + return _canvas.create_text(x, y, fill=color, text=contents, font=font, anchor=anchor) + +def changeText(id, newText, font=None, size=12, style='normal'): + _canvas.itemconfigure(id, text=newText) + if font != None: + _canvas.itemconfigure(id, font=(font, '-%d' % size, style)) + +def changeColor(id, newColor): + _canvas.itemconfigure(id, fill=newColor) + +def line(here, there, color=formatColor(0, 0, 0), width=2): + x0, y0 = here[0], here[1] + x1, y1 = there[0], there[1] + return _canvas.create_line(x0, y0, x1, y1, fill=color, width=width) + +############################################################################## +### Keypress handling ######################################################## +############################################################################## + +# We bind to key-down and key-up events. + +_keysdown = {} +_keyswaiting = {} +# This holds an unprocessed key release. We delay key releases by up to +# one call to keys_pressed() to get round a problem with auto repeat. +_got_release = None + +def _keypress(event): + global _got_release + #remap_arrows(event) + _keysdown[event.keysym] = 1 + _keyswaiting[event.keysym] = 1 +# print(event.char, event.keycode) + _got_release = None + +def _keyrelease(event): + global _got_release + #remap_arrows(event) + try: + del _keysdown[event.keysym] + except: + pass + _got_release = 1 + +def remap_arrows(event): + # TURN ARROW PRESSES INTO LETTERS (SHOULD BE IN KEYBOARD AGENT) + if event.char in ['a', 's', 'd', 'w']: + return + if event.keycode in [37, 101]: # LEFT ARROW (win / x) + event.char = 'a' + if event.keycode in [38, 99]: # UP ARROW + event.char = 'w' + if event.keycode in [39, 102]: # RIGHT ARROW + event.char = 'd' + if event.keycode in [40, 104]: # DOWN ARROW + event.char = 's' + +def _clear_keys(event=None): + global _keysdown, _got_release, _keyswaiting + _keysdown = {} + _keyswaiting = {} + _got_release = None + +def keys_pressed(d_o_e=lambda arg: _root_window.dooneevent(arg), + d_w=tkinter._tkinter.DONT_WAIT): + d_o_e(d_w) + if _got_release: + d_o_e(d_w) + return _keysdown.keys() + +def keys_waiting(): + global _keyswaiting + keys = _keyswaiting.keys() + _keyswaiting = {} + return keys + +# Block for a list of keys... + +def wait_for_keys(): + keys = [] + while keys == []: + keys = keys_pressed() + sleep(0.05) + return keys + +def remove_from_screen(x, + d_o_e=lambda arg: _root_window.dooneevent(arg), + d_w=tkinter._tkinter.DONT_WAIT): + _canvas.delete(x) + d_o_e(d_w) + +def _adjust_coords(coord_list, x, y): + for i in range(0, len(coord_list), 2): + coord_list[i] = coord_list[i] + x + coord_list[i + 1] = coord_list[i + 1] + y + return coord_list + +def move_to(object, x, y=None, + d_o_e=lambda arg: _root_window.dooneevent(arg), + d_w=tkinter._tkinter.DONT_WAIT): + if y is None: + try: x, y = x + except: raise 'incomprehensible coordinates' + + horiz = True + newCoords = [] + current_x, current_y = _canvas.coords(object)[0:2] # first point + for coord in _canvas.coords(object): + if horiz: + inc = x - current_x + else: + inc = y - current_y + horiz = not horiz + + newCoords.append(coord + inc) + + _canvas.coords(object, *newCoords) + d_o_e(d_w) + +def move_by(object, x, y=None, + d_o_e=lambda arg: _root_window.dooneevent(arg), + d_w=tkinter._tkinter.DONT_WAIT, lift=False): + if y is None: + try: x, y = x + except: raise Exception('incomprehensible coordinates') + + horiz = True + newCoords = [] + for coord in _canvas.coords(object): + if horiz: + inc = x + else: + inc = y + horiz = not horiz + + newCoords.append(coord + inc) + + _canvas.coords(object, *newCoords) + d_o_e(d_w) + if lift: + _canvas.tag_raise(object) + +def writePostscript(filename): + "Writes the current canvas to a postscript file." + psfile = open(filename, 'w') + psfile.write(_canvas.postscript(pageanchor='sw', + y='0.c', + x='0.c')) + psfile.close() + +ghost_shape = [ + (0, - 0.5), + (0.25, - 0.75), + (0.5, - 0.5), + (0.75, - 0.75), + (0.75, 0.5), + (0.5, 0.75), + (- 0.5, 0.75), + (- 0.75, 0.5), + (- 0.75, - 0.75), + (- 0.5, - 0.5), + (- 0.25, - 0.75) + ] + +if __name__ == '__main__': + begin_graphics() + clear_screen() + ghost_shape = [(x * 10 + 20, y * 10 + 20) for x, y in ghost_shape] + g = polygon(ghost_shape, formatColor(1, 1, 1)) + move_to(g, (50, 50)) + circle((150, 150), 20, formatColor(0.7, 0.3, 0.0), endpoints=[15, - 15]) + sleep(2) diff --git a/proj1/keyboardAgents.py b/proj1/keyboardAgents.py new file mode 100755 index 0000000..624021d --- /dev/null +++ b/proj1/keyboardAgents.py @@ -0,0 +1,84 @@ +# keyboardAgents.py +# ----------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +from game import Agent +from game import Directions +import random + +class KeyboardAgent(Agent): + """ + An agent controlled by the keyboard. + """ + # NOTE: Arrow keys also work. + WEST_KEY = 'a' + EAST_KEY = 'd' + NORTH_KEY = 'w' + SOUTH_KEY = 's' + STOP_KEY = 'q' + + def __init__( self, index = 0 ): + + self.lastMove = Directions.STOP + self.index = index + self.keys = [] + + def getAction( self, state): + from graphicsUtils import keys_waiting + from graphicsUtils import keys_pressed + keys = list(keys_waiting()) + list(keys_pressed()) + if keys != []: + self.keys = keys + + legal = state.getLegalActions(self.index) + move = self.getMove(legal) + + if move == Directions.STOP: + # Try to move in the same direction as before + if self.lastMove in legal: + move = self.lastMove + + if (self.STOP_KEY in self.keys) and Directions.STOP in legal: move = Directions.STOP + + if move not in legal: + move = random.choice(legal) + + self.lastMove = move + return move + + def getMove(self, legal): + move = Directions.STOP + if (self.WEST_KEY in self.keys or 'Left' in self.keys) and Directions.WEST in legal: move = Directions.WEST + if (self.EAST_KEY in self.keys or 'Right' in self.keys) and Directions.EAST in legal: move = Directions.EAST + if (self.NORTH_KEY in self.keys or 'Up' in self.keys) and Directions.NORTH in legal: move = Directions.NORTH + if (self.SOUTH_KEY in self.keys or 'Down' in self.keys) and Directions.SOUTH in legal: move = Directions.SOUTH + return move + +class KeyboardAgent2(KeyboardAgent): + """ + A second agent controlled by the keyboard. + """ + # NOTE: Arrow keys also work. + WEST_KEY = 'j' + EAST_KEY = "l" + NORTH_KEY = 'i' + SOUTH_KEY = 'k' + STOP_KEY = 'u' + + def getMove(self, legal): + move = Directions.STOP + if (self.WEST_KEY in self.keys) and Directions.WEST in legal: move = Directions.WEST + if (self.EAST_KEY in self.keys) and Directions.EAST in legal: move = Directions.EAST + if (self.NORTH_KEY in self.keys) and Directions.NORTH in legal: move = Directions.NORTH + if (self.SOUTH_KEY in self.keys) and Directions.SOUTH in legal: move = Directions.SOUTH + return move diff --git a/proj1/labtemplate.typ b/proj1/labtemplate.typ new file mode 100755 index 0000000..a43048b --- /dev/null +++ b/proj1/labtemplate.typ @@ -0,0 +1,177 @@ +#let times = "Times LT Pro" +#let times = "Times New Roman" +#let song = (times, "Noto Serif CJK SC") +#let hei = (times, "Noto Sans CJK SC") +#let kai = (times, "Noto Serif CJK SC") +#let xbsong = (times, "Noto Serif CJK SC") +#let fsong = (times, "Noto Serif CJK SC") +#let code = (times, "JetBrains Mono") +#let nudtlabpaper(title: "", + author: "", + id: "", + training_type:"", + grade: "", + major: "", + department: "", + advisor: "", + jobtitle: "", + lab: "", + date: "", + header_str: "", + simple_cover: "", + body) = { + // Set the document's basic properties. + set document(author: author, title: title) + set page( + + margin: (left: 30mm, right: 30mm, top: 30mm, bottom: 30mm), + ) + + // Title row. + v(158pt) + align(center)[ + #block(text(weight: 700, size: 30pt, font: hei, tracking: 15pt, "人工智能实验报告")) + ] + // align(center)[ + // #block(text(weight: 700, size: 30pt, font: song, tracking: 15pt, "本科实验报告")) + // ] + + v(83pt) + pad( + left: 1em, + right: 1em, + grid( + columns: (80pt, 1fr), + rows: (17pt, auto), + text(weight: 700, size: 16pt, font: song, "实验名称:"), + align(center, text(weight: "regular", size: 16pt, font: song, title)), + text(""), + line(length: 100%) + ) + // #block(text(weight: 700, 1.75em, title)) + // underline(text(weight: 700, size: 16pt, font: song, title)) + ) + + // Author information. + + v(82.5pt) + + if simple_cover { + pad( + left: 10%, + right: 10%, + grid( + columns: (100pt, 1fr), + rows: (15pt, 8pt, 15pt, 8pt, 15pt, 8pt), + text(size: 14pt, font: song, "学员姓名:"), + align(center, text(size: 14pt, font: song, author)), + text(""), + line(length: 100%), + text(size: 14pt, font: song, "学 号:"), + align(center, text(size: 14pt, font: times, id)), + text(""), + line(length: 100%), + text(size: 14pt, font: song, "实验日期:"), + align(center, text(size: 14pt, font: song, date)), + text(""), + line(length: 100%), + ) + ) + } else { + grid( + columns: (0.25fr, 0.25fr, 0.25fr, 0.25fr), + rows: (15pt, 8pt, 15pt, 8pt, 15pt, 8pt, 15pt, 8pt, 15pt), + text(size: 14pt, font: song, tracking: 10pt, "学员姓名"), + align(center, text(size: 14pt, font: song, author)), + text(size: 14pt, font: song, tracking: 54pt, "学号"), + align(center, text(size: 14pt, font: times, id)), + text(""), + line(length: 100%), + text(""), + line(length: 100%), + text(size: 14pt, font: song, tracking: 9pt, "培养类型"), + align(center, text(size: 14pt, font: song, training_type)), + text(size: 14pt, font: song, tracking: 54pt, "年级"), + align(center, text(size: 14pt, font: times, grade)), + text(""), + line(length: 100%), + text(""), + line(length: 100%), + text(size: 14pt, font: song, tracking: 54pt, "专业"), + align(center, text(size: 14pt, font: song, major)), + text(size: 14pt, font: song, tracking: 9pt, "所属学院"), + align(center, text(size: 14pt, font: song, department)), + text(""), + line(length: 100%), + text(""), + line(length: 100%), + text(size: 14pt, font: song, tracking: 9pt, "指导教员"), + align(center, text(size: 14pt, font: song, advisor)), + text(size: 14pt, font: song, tracking: 54pt, "职称"), + align(center, text(size: 14pt, font: song, jobtitle)), + text(""), + line(length: 100%), + text(""), + line(length: 100%), + text(size: 14pt, font: song, tracking: 20pt, "实验室"), + align(center, text(size: 14pt, font: song, lab)), + text(size: 14pt, font: song, tracking: 9pt, "实验时间"), + align(center, text(size: 14pt, font: song, date)), + text(""), + line(length: 100%), + text(""), + line(length: 100%), + ) + } + + v(50.5pt) + align(center, text(font: hei, size: 15pt, "国防科技大学教育训练部制")) + + pagebreak() + + set page( + margin: (left: 30mm, right: 30mm, top: 30mm, bottom: 30mm), + numbering: "i", + number-align: center, + ) + + v(14pt) + align(center)[ + #block(text(font: hei, size: 14pt, "《本科实验报告》填写说明")) + ] + + v(14pt) + text("") + par(first-line-indent: 2em, text(font: song, size: 12pt, "实验报告内容编排应符合以下要求:")) + + par(first-line-indent: 2em, text(font: fsong, size: 12pt, "(1)采用A4(21cm×29.7cm)白色复印纸,单面黑字。上下左右各侧的页边距均为3cm;缺省文档网格:字号为小4号,中文为宋体,英文和阿拉伯数字为Times New Roman,每页30行,每行36字;页脚距边界为2.5cm,页码置于页脚、居中,采用小5号阿拉伯数字从1开始连续编排,封面不编页码。")) + + par(first-line-indent: 2em, text(font: fsong, size: 12pt, "(2)报告正文最多可设四级标题,字体均为黑体,第一级标题字号为4号,其余各级标题为小4号;标题序号第一级用“一、”、“二、”……,第二级用“(一)”、“(二)” ……,第三级用“1.”、“2.” ……,第四级用“(1)”、“(2)” ……,分别按序连续编排。")) + + par(first-line-indent: 2em, text(font: fsong, size: 12pt, "(3)正文插图、表格中的文字字号均为5号。")) + + pagebreak() + + set page( + margin: (left: 30mm, right: 30mm, top: 30mm, bottom: 30mm), + numbering: "1", + number-align: center, + ) + + set heading(numbering: "1.1") + // set text(font: hei, lang: "zh") + + show heading: it => box(width: 100%)[ + #v(0.50em) + #set text(font: hei) + #counter(heading).display() + // #h(0.5em) + #it.body + ] + // Main body. + set par(justify: true) + + body +} + +#let para(t) = par(first-line-indent: 2em, text(font: song, size: 10.5pt, t)) \ No newline at end of file diff --git a/proj1/layout.py b/proj1/layout.py new file mode 100755 index 0000000..ebba32a --- /dev/null +++ b/proj1/layout.py @@ -0,0 +1,150 @@ +# layout.py +# --------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +from util import manhattanDistance +from game import Grid +import os +import random +from functools import reduce + +VISIBILITY_MATRIX_CACHE = {} + +class Layout: + """ + A Layout manages the static information about the game board. + """ + + def __init__(self, layoutText): + self.width = len(layoutText[0]) + self.height= len(layoutText) + self.walls = Grid(self.width, self.height, False) + self.food = Grid(self.width, self.height, False) + self.capsules = [] + self.agentPositions = [] + self.numGhosts = 0 + self.processLayoutText(layoutText) + self.layoutText = layoutText + self.totalFood = len(self.food.asList()) + # self.initializeVisibilityMatrix() + + def getNumGhosts(self): + return self.numGhosts + + def initializeVisibilityMatrix(self): + global VISIBILITY_MATRIX_CACHE + if reduce(str.__add__, self.layoutText) not in VISIBILITY_MATRIX_CACHE: + from game import Directions + vecs = [(-0.5,0), (0.5,0),(0,-0.5),(0,0.5)] + dirs = [Directions.NORTH, Directions.SOUTH, Directions.WEST, Directions.EAST] + vis = Grid(self.width, self.height, {Directions.NORTH:set(), Directions.SOUTH:set(), Directions.EAST:set(), Directions.WEST:set(), Directions.STOP:set()}) + for x in range(self.width): + for y in range(self.height): + if self.walls[x][y] == False: + for vec, direction in zip(vecs, dirs): + dx, dy = vec + nextx, nexty = x + dx, y + dy + while (nextx + nexty) != int(nextx) + int(nexty) or not self.walls[int(nextx)][int(nexty)] : + vis[x][y][direction].add((nextx, nexty)) + nextx, nexty = x + dx, y + dy + self.visibility = vis + VISIBILITY_MATRIX_CACHE[reduce(str.__add__, self.layoutText)] = vis + else: + self.visibility = VISIBILITY_MATRIX_CACHE[reduce(str.__add__, self.layoutText)] + + def isWall(self, pos): + x, col = pos + return self.walls[x][col] + + def getRandomLegalPosition(self): + x = random.choice(range(self.width)) + y = random.choice(range(self.height)) + while self.isWall( (x, y) ): + x = random.choice(range(self.width)) + y = random.choice(range(self.height)) + return (x,y) + + def getRandomCorner(self): + poses = [(1,1), (1, self.height - 2), (self.width - 2, 1), (self.width - 2, self.height - 2)] + return random.choice(poses) + + def getFurthestCorner(self, pacPos): + poses = [(1,1), (1, self.height - 2), (self.width - 2, 1), (self.width - 2, self.height - 2)] + dist, pos = max([(manhattanDistance(p, pacPos), p) for p in poses]) + return pos + + def isVisibleFrom(self, ghostPos, pacPos, pacDirection): + row, col = [int(x) for x in pacPos] + return ghostPos in self.visibility[row][col][pacDirection] + + def __str__(self): + return "\n".join(self.layoutText) + + def deepCopy(self): + return Layout(self.layoutText[:]) + + def processLayoutText(self, layoutText): + """ + Coordinates are flipped from the input format to the (x,y) convention here + + The shape of the maze. Each character + represents a different type of object. + % - Wall + . - Food + o - Capsule + G - Ghost + P - Pacman + Other characters are ignored. + """ + maxY = self.height - 1 + for y in range(self.height): + for x in range(self.width): + layoutChar = layoutText[maxY - y][x] + self.processLayoutChar(x, y, layoutChar) + self.agentPositions.sort() + self.agentPositions = [ ( i == 0, pos) for i, pos in self.agentPositions] + + def processLayoutChar(self, x, y, layoutChar): + if layoutChar == '%': + self.walls[x][y] = True + elif layoutChar == '.': + self.food[x][y] = True + elif layoutChar == 'o': + self.capsules.append((x, y)) + elif layoutChar == 'P': + self.agentPositions.append( (0, (x, y) ) ) + elif layoutChar in ['G']: + self.agentPositions.append( (1, (x, y) ) ) + self.numGhosts += 1 + elif layoutChar in ['1', '2', '3', '4']: + self.agentPositions.append( (int(layoutChar), (x,y))) + self.numGhosts += 1 +def getLayout(name, back = 2): + if name.endswith('.lay'): + layout = tryToLoad('layouts/' + name) + if layout == None: layout = tryToLoad(name) + else: + layout = tryToLoad('layouts/' + name + '.lay') + if layout == None: layout = tryToLoad(name + '.lay') + if layout == None and back >= 0: + curdir = os.path.abspath('.') + os.chdir('..') + layout = getLayout(name, back -1) + os.chdir(curdir) + return layout + +def tryToLoad(fullname): + if(not os.path.exists(fullname)): return None + f = open(fullname) + try: return Layout([line.strip() for line in f]) + finally: f.close() diff --git a/proj1/layouts/bigCorners.lay b/proj1/layouts/bigCorners.lay new file mode 100755 index 0000000..4d89d7b --- /dev/null +++ b/proj1/layouts/bigCorners.lay @@ -0,0 +1,37 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%. % %.% +% %%%%% % %%% %%% %%%%%%% % % +% % % % % % % % +%%%%% %%%%% %%% % % % %%% %%%%% % %%% +% % % % % % % % % % % % % +% %%% % % % %%% %%%%% %%% % %%% %%% % +% % % % % % % % % +%%% %%%%%%%%% %%%%%%% %%% %%% % % % % +% % % % % % % +% % %%%%% % %%% % % %%% % %%% %%% % % +% % % % % % % % % % % % % % +% % % %%%%%%% % %%%%%%%%% %%% % %%% % +% % % % % % % % % % +%%% %%% % %%%%% %%%%% %%% %%% %%%%% % +% % % % % % % % % +% % % % % % %%% %%% %%% % % % % % % +% % % % % %% % % % % % % % % % +% % %%%%% % %%% %%% % %%% %%% %%%%% +% % % % % % % % % % % +% %%% % % % %%% %%% %%%%%%%%% % %%% +% % % % % % % +% %%% %%%%%%%%%%%%%%%%%%%%% % % %%% % +% % % % +% % % %%%%% %%% % % % % %%%%%%%%%%%%% +% % % % % % % % % % % % +% % %%% %%% % % % %%%%%%%%% %%% % % % +% % % % % % %P % % % % % % +% %%% %%% %%% % %%% % % %%%%% % %%%%% +% % % % % % % % +%%% % %%%%% %%%%% %%% %%% % %%% % %%% +% % % % % % % % % % % % % % % +% % %%% % % % % %%%%%%%%% % % % % % % +% % % % +% % % %%% %%% %%%%%%% %%% %%% %%% % +%.% % % % % .% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/proj1/layouts/bigMaze.lay b/proj1/layouts/bigMaze.lay new file mode 100755 index 0000000..e11fade --- /dev/null +++ b/proj1/layouts/bigMaze.lay @@ -0,0 +1,37 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % % % % % % % +% %%%%%%% % %%% % %%% %%% %%%%%%% % % +% % % % % % % % +%%%%% %%%%% %%% % % % %%% %%%%% % %%% +% % % % % % % % % % % % % % +% %%% % % % %%% %%%%% %%% % %%% %%% % +% % % % % % % % % +%%% %%%%%%%%% %%%%%%% %%% %%% % % % % +% % % % % % % +% % %%%%% % %%% % % %%% % %%% %%% % % +% % % % % % % % % % % % % % +% % % %%%%%%% % %%%%%%%%% %%% % %%% % +% % % % % % % % % % +%%% %%% % %%%%% %%%%% %%% %%% %%%%% % +% % % % % % % % % % % % +% % % % % %%% %%% %%% %%% % % % % % % +% % % % % % % % % +%%% %%%%%%% % % %%%%% %%% % %%% %%%%% +% % % % % % % % % % +%%%%% % % %%%%%%%%% %%%%%%%%%%% % %%% +% % % % % % % % % +% %%% %%%%% %%%%%%%%% %%%%% % % %%% % +% % % % % % % +% % % %%%%% %%% % % % % %%%%%%%%%%%%% +% % % % % % % % % % % % +% % %%% %%% % % % %%%%%%%%% %%% % % % +% % % % % % % % % % % % % +% %%% %%% %%%%% %%% % % %%%%% % %%%%% +% % % % % % % % % +%%% % %%%%% %%%%% %%% %%% % %%% % %%% +% % % % % % % % % % % % % % % +% % %%% % % % % %%%%%%%%% % % % % % % +% % % % % % +% % % % %%% %%% %%%%%%% %%% %%% %%% % +%.% % % % % % % % P% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/proj1/layouts/bigSafeSearch.lay b/proj1/layouts/bigSafeSearch.lay new file mode 100755 index 0000000..b5fd414 --- /dev/null +++ b/proj1/layouts/bigSafeSearch.lay @@ -0,0 +1,8 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%.%.........%% G % o%%%%.....% +%.%.%%%%%%%.%%%%%% %%%%%%%.%%.% +%............%...%............% +%%%%%...%%%.. ..%.%...%.%%% +%o%%%.%%%%%.%%%%%%%.%%%.%.%%%%% +% ..........Po...%...%. o% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/bigSearch.lay b/proj1/layouts/bigSearch.lay new file mode 100755 index 0000000..bb59eb8 --- /dev/null +++ b/proj1/layouts/bigSearch.lay @@ -0,0 +1,15 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%.....%.................%.....% +%.%%%.%.%%%.%%%%%%%.%%%.%.....% +%.%...%.%......%......%.%.....% +%...%%%.%.%%%%.%.%%%%...%%%...% +%%%.%.%.%.%......%..%.%...%.%%% +%...%.%%%.%.%%% %%%.%.%%%.%...% +%.%%%.......% %.......%%%.% +%...%.%%%%%.%%%%%%%.%.%%%.%...% +%%%.%...%.%....%....%.%...%.%%% +%...%%%.%.%%%%.%.%%%%.%.%%%...% +%.......%......%......%.....%.% +%.....%.%%%.%%%%%%%.%%%.%.%%%.% +%.....%........P....%...%.....% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/boxSearch.lay b/proj1/layouts/boxSearch.lay new file mode 100755 index 0000000..4a113fc --- /dev/null +++ b/proj1/layouts/boxSearch.lay @@ -0,0 +1,14 @@ +%%%%%%%%%%%%%% +%. . . . . % % +% % % +%. . . . . %G% +% % % +%. . . . . % % +% % % +%. . . . . % % +% P %G% +%. . . . . % % +% % % +%. . . . . % % +% % % +%%%%%%%%%%%%%% diff --git a/proj1/layouts/capsuleClassic.lay b/proj1/layouts/capsuleClassic.lay new file mode 100755 index 0000000..06a5c51 --- /dev/null +++ b/proj1/layouts/capsuleClassic.lay @@ -0,0 +1,7 @@ +%%%%%%%%%%%%%%%%%%% +%G. G ....% +%.% % %%%%%% %.%%.% +%.%o% % o% %.o%.% +%.%%%.% %%% %..%.% +%..... P %..%G% +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/contestClassic.lay b/proj1/layouts/contestClassic.lay new file mode 100755 index 0000000..84c8733 --- /dev/null +++ b/proj1/layouts/contestClassic.lay @@ -0,0 +1,9 @@ +%%%%%%%%%%%%%%%%%%%% +%o...%........%...o% +%.%%.%.%%..%%.%.%%.% +%...... G GG%......% +%.%.%%.%% %%%.%%.%.% +%.%....% ooo%.%..%.% +%.%.%%.% %% %.%.%%.% +%o%......P....%....% +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/contoursMaze.lay b/proj1/layouts/contoursMaze.lay new file mode 100755 index 0000000..a068956 --- /dev/null +++ b/proj1/layouts/contoursMaze.lay @@ -0,0 +1,11 @@ +%%%%%%%%%%%%%%%%%%%%% +% % +% % +% % +% % +% P % +% % +% % +% % +%. % +%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/proj1/layouts/greedySearch.lay b/proj1/layouts/greedySearch.lay new file mode 100755 index 0000000..4072363 --- /dev/null +++ b/proj1/layouts/greedySearch.lay @@ -0,0 +1,8 @@ +%%%%%% +%....% +% %%.% +% %%.% +%.P .% +%.%%%% +%....% +%%%%%% \ No newline at end of file diff --git a/proj1/layouts/mediumClassic.lay b/proj1/layouts/mediumClassic.lay new file mode 100755 index 0000000..33c5db8 --- /dev/null +++ b/proj1/layouts/mediumClassic.lay @@ -0,0 +1,11 @@ +%%%%%%%%%%%%%%%%%%%% +%o...%........%....% +%.%%.%.%%%%%%.%.%%.% +%.%..............%.% +%.%.%%.%% %%.%%.%.% +%......%G G%......% +%.%.%%.%%%%%%.%%.%.% +%.%..............%.% +%.%%.%.%%%%%%.%.%%.% +%....%...P....%...o% +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/mediumCorners.lay b/proj1/layouts/mediumCorners.lay new file mode 100755 index 0000000..6a39756 --- /dev/null +++ b/proj1/layouts/mediumCorners.lay @@ -0,0 +1,14 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%. % % % %.% +% % % %%%%%% %%%%%%% % % +% % % % % % +%%%%% %%%%% %%% %% %%%%% % %%% +% % % % % % % % % +% %%% % % % %%%%%%%% %%% %%% % +% % %% % % % % +%%% % %%%%%%% %%%% %%% % % % % +% % %% % % % +% % %%%%% % %%%% % %%% %%% % % +% % % % % % %%% % +%. %P%%%%% % %%% % .% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/proj1/layouts/mediumDottedMaze.lay b/proj1/layouts/mediumDottedMaze.lay new file mode 100755 index 0000000..103f818 --- /dev/null +++ b/proj1/layouts/mediumDottedMaze.lay @@ -0,0 +1,18 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% %%%%%%%%%%%%%%%%%%% %%% %%%%%%%% % +% %% % % %%% %%% %% ... % +% %% % % % % %%%% %%%%%%%%% %% %%%%% +% %% % % % % % %% %% %% ... % +% %% % % % % % %%%% %%% %%%%%% % +% % % % % % %% %%%%%%%% ... % +% %% % % %%%%%%%% %% %% %%%%% +% %% % %% %%%%%%%%% %% ... % +% %%%%%% %%%%%%% %% %%%%%% % +%%%%%% % %%%% %% % ... % +% %%%%%% %%%%% % %% %% %%%%% +% %%%%%% % %%%%% %% % +% %%%%%% %%%%%%%%%%% %% %% % +%%%%%%%%%% %%%%%% % +%. %%%%%%%%%%%%%%%% ...... % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/proj1/layouts/mediumMaze.lay b/proj1/layouts/mediumMaze.lay new file mode 100755 index 0000000..55c1236 --- /dev/null +++ b/proj1/layouts/mediumMaze.lay @@ -0,0 +1,18 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% % +% %% % % %%%%%%% %% % +% %% % % % % %%%% %%%%%%%%% %% %%%%% +% %% % % % % %% %% % +% %% % % % % % %%%% %%% %%%%%% % +% % % % % % %% %%%%%%%% % +% %% % % %%%%%%%% %% %% %%%%% +% %% % %% %%%%%%%%% %% % +% %%%%%% %%%%%%% %% %%%%%% % +%%%%%% % %%%% %% % % +% %%%%%% %%%%% % %% %% %%%%% +% %%%%%% % %%%%% %% % +% %%%%%% %%%%%%%%%%% %% %% % +%%%%%%%%%% %%%%%% % +%. %%%%%%%%%%%%%%%% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/proj1/layouts/mediumSafeSearch.lay b/proj1/layouts/mediumSafeSearch.lay new file mode 100755 index 0000000..e7d6b1c --- /dev/null +++ b/proj1/layouts/mediumSafeSearch.lay @@ -0,0 +1,6 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%.% ....%% G %%%%%% o%%.% +%.%o%%%%%%%.%%%%%%% %%%%%.% +% %%%.%%%%%.%%%%%%%.%%%.%.%%%.% +% ..........Po...%.........% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/mediumScaryMaze.lay b/proj1/layouts/mediumScaryMaze.lay new file mode 100755 index 0000000..65d4c33 --- /dev/null +++ b/proj1/layouts/mediumScaryMaze.lay @@ -0,0 +1,18 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% %%%%%%%%%%%%%%%%%%% %%% %%%%%%%% % +% %% % % %%% %%% %%GG % +% %% % % % % %%%% %%%%%%%%% %% %%%%% +% %% % % % % % %%GG %% % +% %% % % % % % %%%%% %%% %%%%%% % +% %% % % % % %% %%%%%%%%% % +% %% % % %%%%%%%% %% %% %%%%% +% %% % %% %%%%%%%%% %% % +% %%% %% %%%%%%% %% %%%%%% % +%%%%%% % % %% %% % +% %%%%%% %% %% %% %% %%%%% +% %%%%%% % %%%%% %% % +% %%%% %%%%% %%%%%% % +%%%%%%%% % %%%%%% % +%. %%%%%%%%%%%%%%%% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/proj1/layouts/mediumSearch.lay b/proj1/layouts/mediumSearch.lay new file mode 100755 index 0000000..2f8af42 --- /dev/null +++ b/proj1/layouts/mediumSearch.lay @@ -0,0 +1,8 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%............%%%%%............% +%%%.%...%%%.........%.%...%.%%% +%...%%%.%.%%%%.%.%%%%%%.%%%...% +%.%.....%......%......%.....%.% +%.%%%.%%%%%.%%%%%%%.%%%.%.%%%%% +%.....%........P....%...%.....% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/minimaxClassic.lay b/proj1/layouts/minimaxClassic.lay new file mode 100755 index 0000000..a547397 --- /dev/null +++ b/proj1/layouts/minimaxClassic.lay @@ -0,0 +1,5 @@ +%%%%%%%%% +%.P G% +% %.%G%%% +%G %%% +%%%%%%%%% diff --git a/proj1/layouts/oddSearch.lay b/proj1/layouts/oddSearch.lay new file mode 100755 index 0000000..2ddbc9a --- /dev/null +++ b/proj1/layouts/oddSearch.lay @@ -0,0 +1,7 @@ +%%%%%%%%%%%%%%%%%%%% +%...%.........%%...% +%.%.%.%%%%%%%%%%.%.% +%..................% +%%%%%%%%.%.%%%%%%%P% +%%%%%%%%....... % +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/openClassic.lay b/proj1/layouts/openClassic.lay new file mode 100755 index 0000000..6760b42 --- /dev/null +++ b/proj1/layouts/openClassic.lay @@ -0,0 +1,9 @@ +%%%%%%%%%%%%%%%%%%%%%%%%% +%.. P .... .... % +%.. ... ... ... ... % +%.. ... ... ... ... % +%.. .... .... G % +%.. ... ... ... ... % +%.. ... ... ... ... % +%.. .... .... o% +%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/openMaze.lay b/proj1/layouts/openMaze.lay new file mode 100755 index 0000000..5dee689 --- /dev/null +++ b/proj1/layouts/openMaze.lay @@ -0,0 +1,23 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% % % +% % % +% % % +% % % +% % % +% % % % +% % % % +% % % % +% % % % +% % % % +% % % % +% % % % +%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%% +% % % +% % % +% % % +% % +% % +% % +%. % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/proj1/layouts/openSearch.lay b/proj1/layouts/openSearch.lay new file mode 100755 index 0000000..f02d21d --- /dev/null +++ b/proj1/layouts/openSearch.lay @@ -0,0 +1,7 @@ +%%%%%%%%%%%%%%%%%%%% +%..................% +%..................% +%........P.........% +%..................% +%..................% +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/originalClassic.lay b/proj1/layouts/originalClassic.lay new file mode 100755 index 0000000..b2770c5 --- /dev/null +++ b/proj1/layouts/originalClassic.lay @@ -0,0 +1,27 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%............%%............% +%.%%%%.%%%%%.%%.%%%%%.%%%%.% +%o%%%%.%%%%%.%%.%%%%%.%%%%o% +%.%%%%.%%%%%.%%.%%%%%.%%%%.% +%..........................% +%.%%%%.%%.%%%%%%%%.%%.%%%%.% +%.%%%%.%%.%%%%%%%%.%%.%%%%.% +%......%%....%%....%%......% +%%%%%%.%%%%% %% %%%%%.%%%%%% +%%%%%%.%%%%% %% %%%%%.%%%%%% +%%%%%%.% %.%%%%%% +%%%%%%.% %%%% %%%% %.%%%%%% +% . %G GG G% . % +%%%%%%.% %%%%%%%%%% %.%%%%%% +%%%%%%.% %.%%%%%% +%%%%%%.% %%%%%%%%%% %.%%%%%% +%............%%............% +%.%%%%.%%%%%.%%.%%%%%.%%%%.% +%.%%%%.%%%%%.%%.%%%%%.%%%%.% +%o..%%....... .......%%..o% +%%%.%%.%%.%%%%%%%%.%%.%%.%%% +%%%.%%.%%.%%%%%%%%.%%.%%.%%% +%......%%....%%....%%......% +%.%%%%%%%%%%.%%.%%%%%%%%%%.% +%.............P............% +%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/powerClassic.lay b/proj1/layouts/powerClassic.lay new file mode 100755 index 0000000..3f3d983 --- /dev/null +++ b/proj1/layouts/powerClassic.lay @@ -0,0 +1,7 @@ +%%%%%%%%%%%%%%%%%%%% +%o....o%GGGG%o....o% +%..%...%% %%...%..% +%.%o.%........%.o%.% +%.o%.%.%%%%%%.%.%o.% +%........P.........% +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/smallClassic.lay b/proj1/layouts/smallClassic.lay new file mode 100755 index 0000000..ce6c1d9 --- /dev/null +++ b/proj1/layouts/smallClassic.lay @@ -0,0 +1,7 @@ +%%%%%%%%%%%%%%%%%%%% +%......%G G%......% +%.%%...%% %%...%%.% +%.%o.%........%.o%.% +%.%%.%.%%%%%%.%.%%.% +%........P.........% +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/smallMaze.lay b/proj1/layouts/smallMaze.lay new file mode 100755 index 0000000..72d3ffc --- /dev/null +++ b/proj1/layouts/smallMaze.lay @@ -0,0 +1,10 @@ +%%%%%%%%%%%%%%%%%%%%%% +% %% % % % +% %%%%%% % %%%%%% % +%%%%%% P % % +% % %%%%%% %% %%%%% +% %%%% % % % +% %%% %%% % % +%%%%%%%%%% %%%%%% % +%. %% % +%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/proj1/layouts/smallSafeSearch.lay b/proj1/layouts/smallSafeSearch.lay new file mode 100755 index 0000000..b97feaa --- /dev/null +++ b/proj1/layouts/smallSafeSearch.lay @@ -0,0 +1,15 @@ +%%%%%%%%% +%.. % G % +%%% %%%%% +% % +%%%%%%% % +% % +% %%%%% % +% % % +%%%%% % % +% %o% +% %%%%%%% +% .% +%%%%%%%.% +%Po .% +%%%%%%%%% diff --git a/proj1/layouts/smallSearch.lay b/proj1/layouts/smallSearch.lay new file mode 100755 index 0000000..c2321d4 --- /dev/null +++ b/proj1/layouts/smallSearch.lay @@ -0,0 +1,5 @@ +%%%%%%%%%%%%%%%%%%%% +%. ...P .% +%.%%.%%.%%.%%.%% %.% +% %% %..... %.% +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/testClassic.lay b/proj1/layouts/testClassic.lay new file mode 100755 index 0000000..4b3ffca --- /dev/null +++ b/proj1/layouts/testClassic.lay @@ -0,0 +1,10 @@ +%%%%% +% . % +%.G.% +% . % +%. .% +% % +% .% +% % +%P .% +%%%%% diff --git a/proj1/layouts/testMaze.lay b/proj1/layouts/testMaze.lay new file mode 100755 index 0000000..4d259a4 --- /dev/null +++ b/proj1/layouts/testMaze.lay @@ -0,0 +1,3 @@ +%%%%%%%%%% +%. P% +%%%%%%%%%% diff --git a/proj1/layouts/testSearch.lay b/proj1/layouts/testSearch.lay new file mode 100755 index 0000000..25bad23 --- /dev/null +++ b/proj1/layouts/testSearch.lay @@ -0,0 +1,5 @@ +%%%%% +%.P % +%%% % +%. % +%%%%% diff --git a/proj1/layouts/tinyCorners.lay b/proj1/layouts/tinyCorners.lay new file mode 100755 index 0000000..526c880 --- /dev/null +++ b/proj1/layouts/tinyCorners.lay @@ -0,0 +1,8 @@ +%%%%%%%% +%. .% +% P % +% %%%% % +% % % +% % %%%% +%.% .% +%%%%%%%% diff --git a/proj1/layouts/tinyMaze.lay b/proj1/layouts/tinyMaze.lay new file mode 100755 index 0000000..f7035a5 --- /dev/null +++ b/proj1/layouts/tinyMaze.lay @@ -0,0 +1,7 @@ +%%%%%%% +% P% +% %%% % +% % % +%% %% +%. %%%% +%%%%%%% diff --git a/proj1/layouts/tinySafeSearch.lay b/proj1/layouts/tinySafeSearch.lay new file mode 100755 index 0000000..fea6860 --- /dev/null +++ b/proj1/layouts/tinySafeSearch.lay @@ -0,0 +1,7 @@ +%%%%%%%%% +% G %...% +%%%%%%% % +%Po % +%.%%.%%.% +%.%%....% +%%%%%%%%% diff --git a/proj1/layouts/tinySearch.lay b/proj1/layouts/tinySearch.lay new file mode 100755 index 0000000..c51f4b0 --- /dev/null +++ b/proj1/layouts/tinySearch.lay @@ -0,0 +1,7 @@ +%%%%%%%%% +%.. ..% +%%%%.%% % +% P % +%.%% %%.% +%.%. .% +%%%%%%%%% diff --git a/proj1/layouts/trappedClassic.lay b/proj1/layouts/trappedClassic.lay new file mode 100755 index 0000000..289557f --- /dev/null +++ b/proj1/layouts/trappedClassic.lay @@ -0,0 +1,5 @@ +%%%%%%%% +% P G% +%G%%%%%% +%.... % +%%%%%%%% diff --git a/proj1/layouts/trickyClassic.lay b/proj1/layouts/trickyClassic.lay new file mode 100755 index 0000000..ffa156c --- /dev/null +++ b/proj1/layouts/trickyClassic.lay @@ -0,0 +1,13 @@ +%%%%%%%%%%%%%%%%%%%% +%o...%........%...o% +%.%%.%.%%..%%.%.%%.% +%.%.....%..%.....%.% +%.%.%%.%% %%.%%.%.% +%...... GGGG%.%....% +%.%....%%%%%%.%..%.% +%.%....% oo%.%..%.% +%.%....% %%%%.%..%.% +%.%...........%..%.% +%.%%.%.%%%%%%.%.%%.% +%o...%...P....%...o% +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/layouts/trickySearch.lay b/proj1/layouts/trickySearch.lay new file mode 100755 index 0000000..4a607e6 --- /dev/null +++ b/proj1/layouts/trickySearch.lay @@ -0,0 +1,7 @@ +%%%%%%%%%%%%%%%%%%%% +%. ..% % +%.%%.%%.%%.%%.%% % % +% P % % +%%%%%%%%%%%%%%%%%% % +%..... % +%%%%%%%%%%%%%%%%%%%% diff --git a/proj1/main.pdf b/proj1/main.pdf new file mode 100755 index 0000000..30857c2 --- /dev/null +++ b/proj1/main.pdf @@ -0,0 +1,11426 @@ +%PDF-1.7 +% + +1 0 obj +<< + /Type /Pages + /Count 12 + /Kids [752 0 R 754 0 R 770 0 R 772 0 R 774 0 R 776 0 R 778 0 R 780 0 R 782 0 R 784 0 R 786 0 R 788 0 R] +>> +endobj + +2 0 obj +<< + /Type /Outlines + /First 3 0 R + /Last 16 0 R + /Count 6 +>> +endobj + +3 0 obj +<< + /Parent 2 0 R + /Next 4 0 R + /Title + /Dest 738 0 R +>> +endobj + +4 0 obj +<< + /Parent 2 0 R + /Next 5 0 R + /Prev 3 0 R + /Title + /Dest 739 0 R +>> +endobj + +5 0 obj +<< + /Parent 2 0 R + /Next 6 0 R + /Prev 4 0 R + /Title + /Dest 740 0 R +>> +endobj + +6 0 obj +<< + /Parent 2 0 R + /Next 15 0 R + /Prev 5 0 R + /First 7 0 R + /Last 14 0 R + /Count -8 + /Title + /Dest 749 0 R +>> +endobj + +7 0 obj +<< + /Parent 6 0 R + /Next 8 0 R + /Title + /Dest 741 0 R +>> +endobj + +8 0 obj +<< + /Parent 6 0 R + /Next 9 0 R + /Prev 7 0 R + /Title + /Dest 742 0 R +>> +endobj + +9 0 obj +<< + /Parent 6 0 R + /Next 10 0 R + /Prev 8 0 R + /Title + /Dest 743 0 R +>> +endobj + +10 0 obj +<< + /Parent 6 0 R + /Next 11 0 R + /Prev 9 0 R + /Title + /Dest 744 0 R +>> +endobj + +11 0 obj +<< + /Parent 6 0 R + /Next 12 0 R + /Prev 10 0 R + /Title + /Dest 745 0 R +>> +endobj + +12 0 obj +<< + /Parent 6 0 R + /Next 13 0 R + /Prev 11 0 R + /Title + /Dest 746 0 R +>> +endobj + +13 0 obj +<< + /Parent 6 0 R + /Next 14 0 R + /Prev 12 0 R + /Title + /Dest 747 0 R +>> +endobj + +14 0 obj +<< + /Parent 6 0 R + /Prev 13 0 R + /Title + /Dest 748 0 R +>> +endobj + +15 0 obj +<< + /Parent 2 0 R + /Next 16 0 R + /Prev 6 0 R + /Title + /Dest 750 0 R +>> +endobj + +16 0 obj +<< + /Parent 2 0 R + /Prev 15 0 R + /Title + /Dest 751 0 R +>> +endobj + +17 0 obj +<< + /Nums [0 644 0 R 1 645 0 R 2 646 0 R 3 647 0 R 4 648 0 R 5 649 0 R 6 650 0 R 7 651 0 R 8 652 0 R 9 653 0 R 10 654 0 R 11 655 0 R] +>> +endobj + +18 0 obj +<< + /Type /StructTreeRoot + /RoleMap << + /Datetime /Span + /Terms /Part + /Title /P + /Strong /Span + /Em /Span + >> + /K [31 0 R] + /ParentTree << + /Nums [0 19 0 R 1 20 0 R 2 607 0 R 3 603 0 R 4 599 0 R 5 595 0 R 6 591 0 R 7 587 0 R 8 583 0 R 9 579 0 R 10 575 0 R 11 571 0 R 12 567 0 R 13 563 0 R 14 558 0 R 15 554 0 R 16 21 0 R 17 22 0 R 18 23 0 R 19 24 0 R 20 25 0 R 21 26 0 R 22 27 0 R 23 28 0 R 24 29 0 R 25 30 0 R] + >> + /ParentTreeNextKey 26 +>> +endobj + +19 0 obj +[643 0 R 642 0 R 640 0 R 635 0 R 633 0 R 629 0 R 629 0 R 629 0 R 627 0 R 623 0 R 621 0 R 616 0 R] +endobj + +20 0 obj +[615 0 R 614 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 613 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 612 0 R 611 0 R 611 0 R 611 0 R 611 0 R 611 0 R] +endobj + +21 0 obj +[610 0 R 610 0 R 608 0 R 607 0 R 607 0 R 607 0 R 604 0 R 603 0 R 603 0 R 603 0 R 600 0 R 599 0 R 599 0 R 599 0 R 596 0 R 595 0 R 595 0 R 595 0 R 592 0 R 591 0 R 591 0 R 591 0 R 591 0 R 591 0 R 588 0 R 587 0 R 587 0 R 587 0 R 587 0 R 587 0 R 584 0 R 583 0 R 583 0 R 583 0 R 583 0 R 583 0 R 580 0 R 579 0 R 579 0 R 579 0 R 579 0 R 576 0 R 575 0 R 575 0 R 575 0 R 575 0 R 575 0 R 572 0 R 571 0 R 571 0 R 571 0 R 571 0 R 571 0 R 568 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 564 0 R 563 0 R 563 0 R 563 0 R 563 0 R 563 0 R 559 0 R 558 0 R 558 0 R 558 0 R 555 0 R 554 0 R 554 0 R 554 0 R] +endobj + +22 0 obj +[550 0 R 550 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 547 0 R 547 0 R 545 0 R 545 0 R 545 0 R 544 0 R 543 0 R 543 0 R 543 0 R 543 0 R 542 0 R 540 0 R 539 0 R 539 0 R 539 0 R 539 0 R 538 0 R 538 0 R 536 0 R 535 0 R 535 0 R 535 0 R 535 0 R 534 0 R 534 0 R 532 0 R 531 0 R 531 0 R 530 0 R 530 0 R 530 0 R 530 0 R 528 0 R 527 0 R 527 0 R 526 0 R 526 0 R 524 0 R 523 0 R 523 0 R 522 0 R 522 0 R 522 0 R 522 0 R 520 0 R 519 0 R 519 0 R 518 0 R 518 0 R 518 0 R 518 0 R 518 0 R 518 0 R 516 0 R 515 0 R 515 0 R 514 0 R 514 0 R 511 0 R 511 0 R 507 0 R 507 0 R 509 0 R 507 0 R 507 0 R 507 0 R 508 0 R 507 0 R 507 0 R 507 0 R 506 0 R 505 0 R 499 0 R 499 0 R 504 0 R 499 0 R 499 0 R 499 0 R 503 0 R 499 0 R 502 0 R 499 0 R 501 0 R 499 0 R 499 0 R 499 0 R 500 0 R 499 0 R 499 0 R 499 0 R 499 0 R 499 0 R 499 0 R 497 0 R 496 0 R 491 0 R 491 0 R 495 0 R 491 0 R 491 0 R 491 0 R 494 0 R 491 0 R 493 0 R 491 0 R 491 0 R 491 0 R 492 0 R 491 0 R 491 0 R 491 0 R 489 0 R 488 0 R 485 0 R 485 0 R 487 0 R 485 0 R 485 0 R 486 0 R 485 0 R 485 0 R 483 0 R 482 0 R 479 0 R 481 0 R 479 0 R 479 0 R 479 0 R 480 0 R 479 0 R 479 0 R 479 0 R 479 0 R 479 0 R 479 0 R 479 0 R 479 0 R 479 0 R 479 0 R] +endobj + +23 0 obj +[477 0 R 476 0 R 473 0 R 473 0 R 475 0 R 473 0 R 473 0 R 473 0 R 474 0 R 473 0 R 473 0 R 470 0 R 470 0 R 468 0 R 468 0 R 468 0 R 468 0 R 468 0 R 466 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 464 0 R 462 0 R 463 0 R 462 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 459 0 R 459 0 R 459 0 R 459 0 R 459 0 R 459 0 R 459 0 R 459 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 457 0 R 457 0 R 457 0 R 457 0 R 457 0 R 457 0 R 457 0 R 457 0 R 457 0 R 457 0 R 457 0 R 457 0 R 457 0 R 456 0 R 455 0 R 455 0 R 455 0 R 455 0 R 455 0 R 455 0 R 455 0 R 455 0 R 455 0 R 455 0 R 455 0 R 454 0 R 454 0 R 454 0 R 454 0 R 454 0 R 454 0 R 454 0 R 454 0 R 454 0 R 454 0 R 454 0 R 454 0 R 453 0 R 452 0 R 452 0 R 452 0 R 452 0 R 452 0 R 452 0 R 452 0 R 452 0 R 452 0 R 451 0 R 451 0 R 450 0 R 449 0 R 449 0 R 449 0 R 449 0 R 449 0 R 449 0 R 449 0 R 448 0 R 447 0 R 447 0 R 447 0 R 447 0 R 447 0 R 447 0 R 447 0 R 447 0 R 447 0 R 447 0 R 446 0 R 446 0 R 446 0 R 446 0 R 445 0 R 444 0 R 444 0 R 444 0 R 444 0 R 444 0 R 444 0 R 444 0 R 444 0 R 444 0 R 444 0 R 444 0 R 443 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 442 0 R 441 0 R 441 0 R 441 0 R 441 0 R 441 0 R 441 0 R 441 0 R 441 0 R 441 0 R 441 0 R 441 0 R 440 0 R 440 0 R 440 0 R 440 0 R 440 0 R 440 0 R 440 0 R 440 0 R 440 0 R 440 0 R 440 0 R 440 0 R 439 0 R 439 0 R 439 0 R 439 0 R 439 0 R 439 0 R 439 0 R 439 0 R 439 0 R 439 0 R 439 0 R 439 0 R 438 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 435 0 R 434 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 428 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 427 0 R 426 0 R 424 0 R 425 0 R 424 0 R] +endobj + +24 0 obj +[423 0 R 423 0 R 423 0 R 423 0 R 423 0 R 423 0 R 423 0 R 423 0 R 423 0 R 423 0 R 422 0 R 422 0 R 422 0 R 422 0 R 422 0 R 422 0 R 422 0 R 422 0 R 422 0 R 422 0 R 421 0 R 421 0 R 421 0 R 421 0 R 421 0 R 421 0 R 421 0 R 421 0 R 420 0 R 420 0 R 420 0 R 420 0 R 420 0 R 420 0 R 420 0 R 420 0 R 420 0 R 420 0 R 419 0 R 419 0 R 419 0 R 419 0 R 419 0 R 419 0 R 419 0 R 419 0 R 419 0 R 419 0 R 419 0 R 419 0 R 419 0 R 418 0 R 417 0 R 417 0 R 417 0 R 417 0 R 417 0 R 417 0 R 417 0 R 417 0 R 417 0 R 417 0 R 417 0 R 416 0 R 416 0 R 416 0 R 416 0 R 416 0 R 416 0 R 416 0 R 416 0 R 416 0 R 416 0 R 416 0 R 416 0 R 415 0 R 414 0 R 414 0 R 414 0 R 414 0 R 414 0 R 414 0 R 414 0 R 414 0 R 414 0 R 413 0 R 413 0 R 412 0 R 411 0 R 411 0 R 411 0 R 411 0 R 411 0 R 411 0 R 411 0 R 410 0 R 409 0 R 409 0 R 409 0 R 409 0 R 409 0 R 409 0 R 409 0 R 409 0 R 409 0 R 409 0 R 408 0 R 408 0 R 408 0 R 408 0 R 407 0 R 406 0 R 406 0 R 406 0 R 406 0 R 406 0 R 406 0 R 406 0 R 406 0 R 406 0 R 406 0 R 406 0 R 405 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 404 0 R 403 0 R 403 0 R 403 0 R 403 0 R 403 0 R 403 0 R 403 0 R 403 0 R 403 0 R 403 0 R 403 0 R 402 0 R 402 0 R 402 0 R 402 0 R 402 0 R 402 0 R 402 0 R 402 0 R 402 0 R 402 0 R 402 0 R 402 0 R 401 0 R 401 0 R 401 0 R 401 0 R 401 0 R 401 0 R 401 0 R 401 0 R 401 0 R 401 0 R 401 0 R 401 0 R 400 0 R 399 0 R 399 0 R 399 0 R 399 0 R 399 0 R 397 0 R 396 0 R 395 0 R 395 0 R 395 0 R 395 0 R 395 0 R 395 0 R 395 0 R 395 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 392 0 R 392 0 R 392 0 R 392 0 R 392 0 R 390 0 R 389 0 R 389 0 R 389 0 R 389 0 R 389 0 R 389 0 R 389 0 R 389 0 R 389 0 R 388 0 R 386 0 R 387 0 R 386 0 R 385 0 R 385 0 R 385 0 R 385 0 R 385 0 R 385 0 R 385 0 R 385 0 R 385 0 R 385 0 R 384 0 R 384 0 R 384 0 R 384 0 R 384 0 R 384 0 R 384 0 R 384 0 R 384 0 R 384 0 R 383 0 R 383 0 R 383 0 R 383 0 R 383 0 R 383 0 R 383 0 R 382 0 R 382 0 R 382 0 R 382 0 R 382 0 R 382 0 R 382 0 R 382 0 R 382 0 R 382 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 381 0 R 380 0 R 379 0 R 379 0 R 379 0 R 379 0 R 379 0 R 379 0 R 379 0 R 379 0 R 379 0 R 379 0 R 379 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 377 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 375 0 R 375 0 R 374 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 372 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R] +endobj + +25 0 obj +[370 0 R 370 0 R 370 0 R 370 0 R 369 0 R 368 0 R 368 0 R 368 0 R 368 0 R 368 0 R 368 0 R 368 0 R 368 0 R 368 0 R 368 0 R 368 0 R 367 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 365 0 R 365 0 R 365 0 R 365 0 R 365 0 R 365 0 R 365 0 R 365 0 R 365 0 R 365 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 363 0 R 362 0 R 361 0 R 361 0 R 361 0 R 361 0 R 361 0 R 359 0 R 358 0 R 357 0 R 357 0 R 357 0 R 357 0 R 357 0 R 357 0 R 357 0 R 357 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 356 0 R 354 0 R 354 0 R 354 0 R 352 0 R 348 0 R 348 0 R 348 0 R 348 0 R 348 0 R 351 0 R 351 0 R 351 0 R 351 0 R 348 0 R 348 0 R 350 0 R 350 0 R 350 0 R 350 0 R 348 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 349 0 R 348 0 R 348 0 R 348 0 R 347 0 R 345 0 R 346 0 R 345 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 344 0 R 343 0 R 343 0 R 343 0 R 343 0 R 343 0 R 343 0 R 343 0 R 343 0 R 343 0 R 343 0 R 342 0 R 342 0 R 342 0 R 342 0 R 342 0 R 342 0 R 342 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 338 0 R 337 0 R 337 0 R 337 0 R 337 0 R 337 0 R 337 0 R 337 0 R 337 0 R 337 0 R 337 0 R 337 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 335 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 333 0 R 333 0 R 332 0 R 331 0 R 331 0 R 331 0 R 331 0 R 331 0 R 331 0 R 331 0 R 331 0 R 331 0 R 330 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 328 0 R 328 0 R 328 0 R 328 0 R 327 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 325 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 324 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 322 0 R 322 0 R 322 0 R 322 0 R 322 0 R 322 0 R 322 0 R 322 0 R 322 0 R 322 0 R 322 0 R 322 0 R 321 0 R 321 0 R 321 0 R 321 0 R 321 0 R 321 0 R 321 0 R 321 0 R 321 0 R 321 0 R 320 0 R 320 0 R 320 0 R 320 0 R 320 0 R 320 0 R 320 0 R 320 0 R 320 0 R 320 0 R 320 0 R 320 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 319 0 R 318 0 R 317 0 R 317 0 R 317 0 R 317 0 R 317 0 R 315 0 R 314 0 R] +endobj + +26 0 obj +[313 0 R 313 0 R 313 0 R 313 0 R 313 0 R 313 0 R 313 0 R 313 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 312 0 R 310 0 R 310 0 R 310 0 R 310 0 R 310 0 R 308 0 R 303 0 R 303 0 R 303 0 R 303 0 R 303 0 R 307 0 R 303 0 R 303 0 R 306 0 R 303 0 R 303 0 R 303 0 R 305 0 R 303 0 R 303 0 R 304 0 R 303 0 R 303 0 R 303 0 R 302 0 R 300 0 R 301 0 R 300 0 R 299 0 R 299 0 R 299 0 R 299 0 R 299 0 R 299 0 R 299 0 R 299 0 R 299 0 R 299 0 R 299 0 R 298 0 R 298 0 R 298 0 R 298 0 R 297 0 R 297 0 R 297 0 R 297 0 R 297 0 R 297 0 R 297 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 296 0 R 295 0 R 295 0 R 295 0 R 295 0 R 295 0 R 295 0 R 295 0 R 295 0 R 295 0 R 295 0 R 295 0 R 293 0 R 293 0 R 293 0 R 293 0 R 293 0 R 293 0 R 293 0 R 292 0 R 292 0 R 292 0 R 292 0 R 292 0 R 292 0 R 292 0 R 292 0 R 292 0 R 292 0 R 292 0 R 292 0 R 292 0 R 291 0 R 291 0 R 291 0 R 291 0 R 291 0 R 291 0 R 291 0 R 291 0 R 290 0 R 290 0 R 290 0 R 290 0 R 290 0 R 290 0 R 290 0 R 288 0 R 288 0 R 288 0 R 288 0 R 288 0 R 288 0 R 288 0 R 288 0 R 288 0 R 287 0 R 287 0 R 287 0 R 287 0 R 287 0 R 287 0 R 287 0 R 287 0 R 287 0 R 287 0 R 287 0 R 287 0 R 287 0 R 286 0 R 286 0 R 286 0 R 286 0 R 286 0 R 286 0 R 286 0 R 285 0 R 285 0 R 285 0 R 285 0 R 285 0 R 285 0 R 285 0 R 285 0 R 284 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 283 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 279 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 276 0 R 276 0 R 276 0 R 276 0 R 276 0 R 276 0 R 276 0 R 276 0 R 276 0 R 275 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 271 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 270 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 269 0 R 267 0 R 267 0 R 267 0 R 267 0 R 267 0 R 267 0 R 267 0 R 267 0 R 266 0 R 266 0 R 266 0 R 266 0 R 264 0 R 263 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R] +endobj + +27 0 obj +[259 0 R 259 0 R 259 0 R 259 0 R 259 0 R 257 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 256 0 R 255 0 R 253 0 R 254 0 R 253 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 252 0 R 251 0 R 251 0 R 251 0 R 251 0 R 251 0 R 251 0 R 251 0 R 251 0 R 250 0 R 250 0 R 250 0 R 250 0 R 250 0 R 250 0 R 250 0 R 250 0 R 249 0 R 248 0 R 248 0 R 248 0 R 248 0 R 248 0 R 248 0 R 248 0 R 248 0 R 247 0 R 247 0 R 247 0 R 247 0 R 246 0 R 245 0 R 245 0 R 245 0 R 245 0 R 245 0 R 245 0 R 245 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 244 0 R 243 0 R 243 0 R 243 0 R 243 0 R 243 0 R 243 0 R 243 0 R 243 0 R 243 0 R 243 0 R 242 0 R 242 0 R 242 0 R 242 0 R 242 0 R 242 0 R 242 0 R 241 0 R 240 0 R 240 0 R 240 0 R 240 0 R 240 0 R 240 0 R 240 0 R 239 0 R 239 0 R 239 0 R 239 0 R 238 0 R 237 0 R 237 0 R 237 0 R 237 0 R 237 0 R 237 0 R 236 0 R 236 0 R 236 0 R 236 0 R 236 0 R 236 0 R 236 0 R 236 0 R 236 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 234 0 R 234 0 R 234 0 R 234 0 R 234 0 R 234 0 R 234 0 R 234 0 R 234 0 R 234 0 R 234 0 R 234 0 R 233 0 R 232 0 R 232 0 R 232 0 R 232 0 R 232 0 R 232 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 231 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 230 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 229 0 R 228 0 R 228 0 R 228 0 R 228 0 R 228 0 R 228 0 R 228 0 R 228 0 R 228 0 R 228 0 R 228 0 R 228 0 R 227 0 R 226 0 R 226 0 R 226 0 R 226 0 R 226 0 R 226 0 R 226 0 R 226 0 R 226 0 R 226 0 R 224 0 R 223 0 R 222 0 R 222 0 R 222 0 R 222 0 R 222 0 R 222 0 R 222 0 R 222 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 221 0 R 219 0 R 219 0 R 219 0 R 219 0 R 219 0 R 217 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 216 0 R 215 0 R 213 0 R 214 0 R 213 0 R] +endobj + +28 0 obj +[212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 212 0 R 211 0 R 211 0 R 211 0 R 211 0 R 211 0 R 211 0 R 211 0 R 211 0 R 210 0 R 210 0 R 210 0 R 210 0 R 210 0 R 210 0 R 210 0 R 210 0 R 210 0 R 210 0 R 209 0 R 208 0 R 208 0 R 208 0 R 208 0 R 208 0 R 208 0 R 208 0 R 207 0 R 207 0 R 207 0 R 207 0 R 206 0 R 205 0 R 205 0 R 205 0 R 205 0 R 205 0 R 205 0 R 204 0 R 204 0 R 204 0 R 204 0 R 204 0 R 204 0 R 204 0 R 204 0 R 204 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 203 0 R 202 0 R 202 0 R 202 0 R 202 0 R 202 0 R 202 0 R 202 0 R 202 0 R 202 0 R 202 0 R 202 0 R 202 0 R 201 0 R 200 0 R 200 0 R 200 0 R 200 0 R 200 0 R 200 0 R 199 0 R 199 0 R 199 0 R 199 0 R 199 0 R 199 0 R 199 0 R 199 0 R 199 0 R 199 0 R 199 0 R 199 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 198 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 197 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 196 0 R 195 0 R 195 0 R 195 0 R 195 0 R 195 0 R 195 0 R 195 0 R 195 0 R 195 0 R 195 0 R 195 0 R 195 0 R 194 0 R 193 0 R 193 0 R 193 0 R 193 0 R 193 0 R 193 0 R 193 0 R 193 0 R 193 0 R 192 0 R 191 0 R 191 0 R 191 0 R 191 0 R 191 0 R 191 0 R 191 0 R 191 0 R 191 0 R 191 0 R 191 0 R 191 0 R 191 0 R 189 0 R 188 0 R 187 0 R 187 0 R 187 0 R 187 0 R 187 0 R 187 0 R 187 0 R 187 0 R 186 0 R 186 0 R 186 0 R 186 0 R 186 0 R 186 0 R 186 0 R 186 0 R 186 0 R 186 0 R 186 0 R 186 0 R 186 0 R 184 0 R 184 0 R 184 0 R 184 0 R 184 0 R 182 0 R 179 0 R 179 0 R 179 0 R 179 0 R 181 0 R 179 0 R 179 0 R 179 0 R 180 0 R 179 0 R 179 0 R 179 0 R 179 0 R 178 0 R 176 0 R 177 0 R 176 0 R 175 0 R 175 0 R 175 0 R 175 0 R 175 0 R 175 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 174 0 R 173 0 R 173 0 R 173 0 R 173 0 R 173 0 R 173 0 R 173 0 R 173 0 R 172 0 R 172 0 R 172 0 R 172 0 R 172 0 R 172 0 R 172 0 R 172 0 R 172 0 R 172 0 R 172 0 R 172 0 R 170 0 R 170 0 R 170 0 R 170 0 R 170 0 R 170 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 169 0 R 168 0 R 168 0 R 168 0 R 168 0 R 168 0 R 168 0 R 168 0 R 168 0 R 168 0 R 167 0 R 167 0 R 167 0 R 167 0 R 167 0 R 167 0 R 167 0 R 167 0 R 166 0 R 166 0 R 166 0 R 166 0 R 166 0 R 166 0 R 166 0 R 166 0 R 166 0 R 166 0 R 166 0 R 165 0 R 165 0 R 165 0 R 165 0 R 163 0 R 162 0 R 161 0 R 161 0 R 161 0 R 161 0 R 161 0 R 161 0 R 161 0 R 161 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R 160 0 R] +endobj + +29 0 obj +[158 0 R 158 0 R 155 0 R 155 0 R 155 0 R 155 0 R 155 0 R 155 0 R 155 0 R 155 0 R 156 0 R 155 0 R 155 0 R 154 0 R 153 0 R 152 0 R 151 0 R 150 0 R 149 0 R 148 0 R 147 0 R 146 0 R 145 0 R 144 0 R 143 0 R 141 0 R 140 0 R 139 0 R 139 0 R 135 0 R 134 0 R 132 0 R 130 0 R 130 0 R 131 0 R 130 0 R 130 0 R 130 0 R 130 0 R 130 0 R 130 0 R 127 0 R 126 0 R 126 0 R 122 0 R 121 0 R 119 0 R 117 0 R 117 0 R 118 0 R 117 0 R 117 0 R 117 0 R 117 0 R 117 0 R 117 0 R 114 0 R 113 0 R 113 0 R 109 0 R 108 0 R 106 0 R 103 0 R 105 0 R 103 0 R 104 0 R 103 0 R 100 0 R 99 0 R 99 0 R 95 0 R 94 0 R 92 0 R 90 0 R 90 0 R 91 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 87 0 R 86 0 R 86 0 R 82 0 R 81 0 R 79 0 R 77 0 R 77 0 R 78 0 R 77 0 R 77 0 R 77 0 R 77 0 R 77 0 R 77 0 R 74 0 R 73 0 R 73 0 R 69 0 R 68 0 R 66 0 R 63 0 R 63 0 R 65 0 R 63 0 R 63 0 R 63 0 R 63 0 R 63 0 R 64 0 R 63 0 R 63 0 R 63 0 R 60 0 R 59 0 R 59 0 R 55 0 R 54 0 R 54 0 R 52 0 R 49 0 R 49 0 R 51 0 R 49 0 R 49 0 R 49 0 R 50 0 R 49 0 R 49 0 R 49 0 R 49 0 R 46 0 R 45 0 R 45 0 R 41 0 R 40 0 R] +endobj + +30 0 obj +[36 0 R 36 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 34 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 33 0 R 32 0 R 32 0 R 32 0 R 32 0 R 32 0 R] +endobj + +31 0 obj +<< + /Type /StructElem + /S /Document + /P 18 0 R + /K [643 0 R 636 0 R 617 0 R 616 0 R 615 0 R 614 0 R 613 0 R 612 0 R 611 0 R 609 0 R 551 0 R 549 0 R 548 0 R 546 0 R 545 0 R 512 0 R 510 0 R 507 0 R 471 0 R 469 0 R 467 0 R 465 0 R 462 0 R 436 0 R 434 0 R 431 0 R 429 0 R 427 0 R 424 0 R 398 0 R 396 0 R 393 0 R 391 0 R 389 0 R 386 0 R 360 0 R 358 0 R 355 0 R 353 0 R 348 0 R 345 0 R 316 0 R 314 0 R 311 0 R 309 0 R 303 0 R 300 0 R 265 0 R 263 0 R 260 0 R 258 0 R 256 0 R 253 0 R 225 0 R 223 0 R 220 0 R 218 0 R 216 0 R 213 0 R 190 0 R 188 0 R 185 0 R 183 0 R 179 0 R 176 0 R 164 0 R 162 0 R 159 0 R 157 0 R 155 0 R 142 0 R 141 0 R 37 0 R 35 0 R 34 0 R 33 0 R 32 0 R] +>> +endobj + +32 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [43 44 45 46 47] + /Pg 788 0 R +>> +endobj + +33 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42] + /Pg 788 0 R +>> +endobj + +34 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17] + /Pg 788 0 R +>> +endobj + +35 0 obj +<< + /Type /StructElem + /S /H1 + /P 31 0 R + /T + /K [36 0 R] +>> +endobj + +36 0 obj +<< + /Type /StructElem + /S /P + /P 35 0 R + /K [0 1] + /Pg 788 0 R +>> +endobj + +37 0 obj +<< + /Type /StructElem + /S /L + /P 31 0 R + /A [<< + /O /List + /ListNumbering /Decimal + >>] + /K [136 0 R 128 0 R 123 0 R 115 0 R 110 0 R 101 0 R 96 0 R 88 0 R 83 0 R 75 0 R 70 0 R 61 0 R 56 0 R 47 0 R 42 0 R 38 0 R] +>> +endobj + +38 0 obj +<< + /Type /StructElem + /S /L + /P 37 0 R + /A [<< + /O /List + /ListNumbering /Circle + >>] + /K [39 0 R] +>> +endobj + +39 0 obj +<< + /Type /StructElem + /S /LI + /P 38 0 R + /K [41 0 R 40 0 R] +>> +endobj + +40 0 obj +<< + /Type /StructElem + /S /LBody + /P 39 0 R + /K [143] + /Pg 786 0 R +>> +endobj + +41 0 obj +<< + /Type /StructElem + /S /Lbl + /P 39 0 R + /K [142] + /Pg 786 0 R +>> +endobj + +42 0 obj +<< + /Type /StructElem + /S /LI + /P 37 0 R + /K [46 0 R 43 0 R] +>> +endobj + +43 0 obj +<< + /Type /StructElem + /S /LBody + /P 42 0 R + /K [44 0 R] +>> +endobj + +44 0 obj +<< + /Type /StructElem + /S /P + /P 43 0 R + /K [45 0 R] +>> +endobj + +45 0 obj +<< + /Type /StructElem + /S /Strong + /P 44 0 R + /K [140 141] + /Pg 786 0 R +>> +endobj + +46 0 obj +<< + /Type /StructElem + /S /Lbl + /P 42 0 R + /K [139] + /Pg 786 0 R +>> +endobj + +47 0 obj +<< + /Type /StructElem + /S /L + /P 37 0 R + /A [<< + /O /List + /ListNumbering /Circle + >>] + /K [53 0 R 48 0 R] +>> +endobj + +48 0 obj +<< + /Type /StructElem + /S /LI + /P 47 0 R + /K [52 0 R 49 0 R] +>> +endobj + +49 0 obj +<< + /Type /StructElem + /S /LBody + /P 48 0 R + /K [128 129 51 0 R 131 132 133 50 0 R 135 136 137 138] + /Pg 786 0 R +>> +endobj + +50 0 obj +<< + /Type /StructElem + /S /Strong + /P 49 0 R + /K [134] + /Pg 786 0 R +>> +endobj + +51 0 obj +<< + /Type /StructElem + /S /Code + /P 49 0 R + /K [130] + /Pg 786 0 R +>> +endobj + +52 0 obj +<< + /Type /StructElem + /S /Lbl + /P 48 0 R + /K [127] + /Pg 786 0 R +>> +endobj + +53 0 obj +<< + /Type /StructElem + /S /LI + /P 47 0 R + /K [55 0 R 54 0 R] +>> +endobj + +54 0 obj +<< + /Type /StructElem + /S /LBody + /P 53 0 R + /K [125 126] + /Pg 786 0 R +>> +endobj + +55 0 obj +<< + /Type /StructElem + /S /Lbl + /P 53 0 R + /K [124] + /Pg 786 0 R +>> +endobj + +56 0 obj +<< + /Type /StructElem + /S /LI + /P 37 0 R + /K [60 0 R 57 0 R] +>> +endobj + +57 0 obj +<< + /Type /StructElem + /S /LBody + /P 56 0 R + /K [58 0 R] +>> +endobj + +58 0 obj +<< + /Type /StructElem + /S /P + /P 57 0 R + /K [59 0 R] +>> +endobj + +59 0 obj +<< + /Type /StructElem + /S /Strong + /P 58 0 R + /K [122 123] + /Pg 786 0 R +>> +endobj + +60 0 obj +<< + /Type /StructElem + /S /Lbl + /P 56 0 R + /K [121] + /Pg 786 0 R +>> +endobj + +61 0 obj +<< + /Type /StructElem + /S /L + /P 37 0 R + /A [<< + /O /List + /ListNumbering /Circle + >>] + /K [67 0 R 62 0 R] +>> +endobj + +62 0 obj +<< + /Type /StructElem + /S /LI + /P 61 0 R + /K [66 0 R 63 0 R] +>> +endobj + +63 0 obj +<< + /Type /StructElem + /S /LBody + /P 62 0 R + /K [109 110 65 0 R 112 113 114 115 116 64 0 R 118 119 120] + /Pg 786 0 R +>> +endobj + +64 0 obj +<< + /Type /StructElem + /S /Strong + /P 63 0 R + /K [117] + /Pg 786 0 R +>> +endobj + +65 0 obj +<< + /Type /StructElem + /S /Code + /P 63 0 R + /K [111] + /Pg 786 0 R +>> +endobj + +66 0 obj +<< + /Type /StructElem + /S /Lbl + /P 62 0 R + /K [108] + /Pg 786 0 R +>> +endobj + +67 0 obj +<< + /Type /StructElem + /S /LI + /P 61 0 R + /K [69 0 R 68 0 R] +>> +endobj + +68 0 obj +<< + /Type /StructElem + /S /LBody + /P 67 0 R + /K [107] + /Pg 786 0 R +>> +endobj + +69 0 obj +<< + /Type /StructElem + /S /Lbl + /P 67 0 R + /K [106] + /Pg 786 0 R +>> +endobj + +70 0 obj +<< + /Type /StructElem + /S /LI + /P 37 0 R + /K [74 0 R 71 0 R] +>> +endobj + +71 0 obj +<< + /Type /StructElem + /S /LBody + /P 70 0 R + /K [72 0 R] +>> +endobj + +72 0 obj +<< + /Type /StructElem + /S /P + /P 71 0 R + /K [73 0 R] +>> +endobj + +73 0 obj +<< + /Type /StructElem + /S /Strong + /P 72 0 R + /K [104 105] + /Pg 786 0 R +>> +endobj + +74 0 obj +<< + /Type /StructElem + /S /Lbl + /P 70 0 R + /K [103] + /Pg 786 0 R +>> +endobj + +75 0 obj +<< + /Type /StructElem + /S /L + /P 37 0 R + /A [<< + /O /List + /ListNumbering /Circle + >>] + /K [80 0 R 76 0 R] +>> +endobj + +76 0 obj +<< + /Type /StructElem + /S /LI + /P 75 0 R + /K [79 0 R 77 0 R] +>> +endobj + +77 0 obj +<< + /Type /StructElem + /S /LBody + /P 76 0 R + /K [94 95 78 0 R 97 98 99 100 101 102] + /Pg 786 0 R +>> +endobj + +78 0 obj +<< + /Type /StructElem + /S /Code + /P 77 0 R + /K [96] + /Pg 786 0 R +>> +endobj + +79 0 obj +<< + /Type /StructElem + /S /Lbl + /P 76 0 R + /K [93] + /Pg 786 0 R +>> +endobj + +80 0 obj +<< + /Type /StructElem + /S /LI + /P 75 0 R + /K [82 0 R 81 0 R] +>> +endobj + +81 0 obj +<< + /Type /StructElem + /S /LBody + /P 80 0 R + /K [92] + /Pg 786 0 R +>> +endobj + +82 0 obj +<< + /Type /StructElem + /S /Lbl + /P 80 0 R + /K [91] + /Pg 786 0 R +>> +endobj + +83 0 obj +<< + /Type /StructElem + /S /LI + /P 37 0 R + /K [87 0 R 84 0 R] +>> +endobj + +84 0 obj +<< + /Type /StructElem + /S /LBody + /P 83 0 R + /K [85 0 R] +>> +endobj + +85 0 obj +<< + /Type /StructElem + /S /P + /P 84 0 R + /K [86 0 R] +>> +endobj + +86 0 obj +<< + /Type /StructElem + /S /Strong + /P 85 0 R + /K [89 90] + /Pg 786 0 R +>> +endobj + +87 0 obj +<< + /Type /StructElem + /S /Lbl + /P 83 0 R + /K [88] + /Pg 786 0 R +>> +endobj + +88 0 obj +<< + /Type /StructElem + /S /L + /P 37 0 R + /A [<< + /O /List + /ListNumbering /Circle + >>] + /K [93 0 R 89 0 R] +>> +endobj + +89 0 obj +<< + /Type /StructElem + /S /LI + /P 88 0 R + /K [92 0 R 90 0 R] +>> +endobj + +90 0 obj +<< + /Type /StructElem + /S /LBody + /P 89 0 R + /K [73 74 91 0 R 76 77 78 79 80 81 82 83 84 85 86 87] + /Pg 786 0 R +>> +endobj + +91 0 obj +<< + /Type /StructElem + /S /Code + /P 90 0 R + /K [75] + /Pg 786 0 R +>> +endobj + +92 0 obj +<< + /Type /StructElem + /S /Lbl + /P 89 0 R + /K [72] + /Pg 786 0 R +>> +endobj + +93 0 obj +<< + /Type /StructElem + /S /LI + /P 88 0 R + /K [95 0 R 94 0 R] +>> +endobj + +94 0 obj +<< + /Type /StructElem + /S /LBody + /P 93 0 R + /K [71] + /Pg 786 0 R +>> +endobj + +95 0 obj +<< + /Type /StructElem + /S /Lbl + /P 93 0 R + /K [70] + /Pg 786 0 R +>> +endobj + +96 0 obj +<< + /Type /StructElem + /S /LI + /P 37 0 R + /K [100 0 R 97 0 R] +>> +endobj + +97 0 obj +<< + /Type /StructElem + /S /LBody + /P 96 0 R + /K [98 0 R] +>> +endobj + +98 0 obj +<< + /Type /StructElem + /S /P + /P 97 0 R + /K [99 0 R] +>> +endobj + +99 0 obj +<< + /Type /StructElem + /S /Strong + /P 98 0 R + /K [68 69] + /Pg 786 0 R +>> +endobj + +100 0 obj +<< + /Type /StructElem + /S /Lbl + /P 96 0 R + /K [67] + /Pg 786 0 R +>> +endobj + +101 0 obj +<< + /Type /StructElem + /S /L + /P 37 0 R + /A [<< + /O /List + /ListNumbering /Circle + >>] + /K [107 0 R 102 0 R] +>> +endobj + +102 0 obj +<< + /Type /StructElem + /S /LI + /P 101 0 R + /K [106 0 R 103 0 R] +>> +endobj + +103 0 obj +<< + /Type /StructElem + /S /LBody + /P 102 0 R + /K [62 105 0 R 64 104 0 R 66] + /Pg 786 0 R +>> +endobj + +104 0 obj +<< + /Type /StructElem + /S /Code + /P 103 0 R + /K [65] + /Pg 786 0 R +>> +endobj + +105 0 obj +<< + /Type /StructElem + /S /Code + /P 103 0 R + /K [63] + /Pg 786 0 R +>> +endobj + +106 0 obj +<< + /Type /StructElem + /S /Lbl + /P 102 0 R + /K [61] + /Pg 786 0 R +>> +endobj + +107 0 obj +<< + /Type /StructElem + /S /LI + /P 101 0 R + /K [109 0 R 108 0 R] +>> +endobj + +108 0 obj +<< + /Type /StructElem + /S /LBody + /P 107 0 R + /K [60] + /Pg 786 0 R +>> +endobj + +109 0 obj +<< + /Type /StructElem + /S /Lbl + /P 107 0 R + /K [59] + /Pg 786 0 R +>> +endobj + +110 0 obj +<< + /Type /StructElem + /S /LI + /P 37 0 R + /K [114 0 R 111 0 R] +>> +endobj + +111 0 obj +<< + /Type /StructElem + /S /LBody + /P 110 0 R + /K [112 0 R] +>> +endobj + +112 0 obj +<< + /Type /StructElem + /S /P + /P 111 0 R + /K [113 0 R] +>> +endobj + +113 0 obj +<< + /Type /StructElem + /S /Strong + /P 112 0 R + /K [57 58] + /Pg 786 0 R +>> +endobj + +114 0 obj +<< + /Type /StructElem + /S /Lbl + /P 110 0 R + /K [56] + /Pg 786 0 R +>> +endobj + +115 0 obj +<< + /Type /StructElem + /S /L + /P 37 0 R + /A [<< + /O /List + /ListNumbering /Circle + >>] + /K [120 0 R 116 0 R] +>> +endobj + +116 0 obj +<< + /Type /StructElem + /S /LI + /P 115 0 R + /K [119 0 R 117 0 R] +>> +endobj + +117 0 obj +<< + /Type /StructElem + /S /LBody + /P 116 0 R + /K [47 48 118 0 R 50 51 52 53 54 55] + /Pg 786 0 R +>> +endobj + +118 0 obj +<< + /Type /StructElem + /S /Code + /P 117 0 R + /K [49] + /Pg 786 0 R +>> +endobj + +119 0 obj +<< + /Type /StructElem + /S /Lbl + /P 116 0 R + /K [46] + /Pg 786 0 R +>> +endobj + +120 0 obj +<< + /Type /StructElem + /S /LI + /P 115 0 R + /K [122 0 R 121 0 R] +>> +endobj + +121 0 obj +<< + /Type /StructElem + /S /LBody + /P 120 0 R + /K [45] + /Pg 786 0 R +>> +endobj + +122 0 obj +<< + /Type /StructElem + /S /Lbl + /P 120 0 R + /K [44] + /Pg 786 0 R +>> +endobj + +123 0 obj +<< + /Type /StructElem + /S /LI + /P 37 0 R + /K [127 0 R 124 0 R] +>> +endobj + +124 0 obj +<< + /Type /StructElem + /S /LBody + /P 123 0 R + /K [125 0 R] +>> +endobj + +125 0 obj +<< + /Type /StructElem + /S /P + /P 124 0 R + /K [126 0 R] +>> +endobj + +126 0 obj +<< + /Type /StructElem + /S /Strong + /P 125 0 R + /K [42 43] + /Pg 786 0 R +>> +endobj + +127 0 obj +<< + /Type /StructElem + /S /Lbl + /P 123 0 R + /K [41] + /Pg 786 0 R +>> +endobj + +128 0 obj +<< + /Type /StructElem + /S /L + /P 37 0 R + /A [<< + /O /List + /ListNumbering /Circle + >>] + /K [133 0 R 129 0 R] +>> +endobj + +129 0 obj +<< + /Type /StructElem + /S /LI + /P 128 0 R + /K [132 0 R 130 0 R] +>> +endobj + +130 0 obj +<< + /Type /StructElem + /S /LBody + /P 129 0 R + /K [32 33 131 0 R 35 36 37 38 39 40] + /Pg 786 0 R +>> +endobj + +131 0 obj +<< + /Type /StructElem + /S /Code + /P 130 0 R + /K [34] + /Pg 786 0 R +>> +endobj + +132 0 obj +<< + /Type /StructElem + /S /Lbl + /P 129 0 R + /K [31] + /Pg 786 0 R +>> +endobj + +133 0 obj +<< + /Type /StructElem + /S /LI + /P 128 0 R + /K [135 0 R 134 0 R] +>> +endobj + +134 0 obj +<< + /Type /StructElem + /S /LBody + /P 133 0 R + /K [30] + /Pg 786 0 R +>> +endobj + +135 0 obj +<< + /Type /StructElem + /S /Lbl + /P 133 0 R + /K [29] + /Pg 786 0 R +>> +endobj + +136 0 obj +<< + /Type /StructElem + /S /LI + /P 37 0 R + /K [140 0 R 137 0 R] +>> +endobj + +137 0 obj +<< + /Type /StructElem + /S /LBody + /P 136 0 R + /K [138 0 R] +>> +endobj + +138 0 obj +<< + /Type /StructElem + /S /P + /P 137 0 R + /K [139 0 R] +>> +endobj + +139 0 obj +<< + /Type /StructElem + /S /Strong + /P 138 0 R + /K [27 28] + /Pg 786 0 R +>> +endobj + +140 0 obj +<< + /Type /StructElem + /S /Lbl + /P 136 0 R + /K [26] + /Pg 786 0 R +>> +endobj + +141 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [25] + /Pg 786 0 R +>> +endobj + +142 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [154 0 R 153 0 R 152 0 R 151 0 R 150 0 R 149 0 R 148 0 R 147 0 R 146 0 R 145 0 R 144 0 R 143 0 R] +>> +endobj + +143 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [24] + /Pg 786 0 R +>> +endobj + +144 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [23] + /Pg 786 0 R +>> +endobj + +145 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [22] + /Pg 786 0 R +>> +endobj + +146 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [21] + /Pg 786 0 R +>> +endobj + +147 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [20] + /Pg 786 0 R +>> +endobj + +148 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [19] + /Pg 786 0 R +>> +endobj + +149 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [18] + /Pg 786 0 R +>> +endobj + +150 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [17] + /Pg 786 0 R +>> +endobj + +151 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [16] + /Pg 786 0 R +>> +endobj + +152 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [15] + /Pg 786 0 R +>> +endobj + +153 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [14] + /Pg 786 0 R +>> +endobj + +154 0 obj +<< + /Type /StructElem + /S /P + /P 142 0 R + /K [13] + /Pg 786 0 R +>> +endobj + +155 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [2 3 4 5 6 7 8 9 156 0 R 11 12] + /Pg 786 0 R +>> +endobj + +156 0 obj +<< + /Type /StructElem + /S /Strong + /P 155 0 R + /K [10] + /Pg 786 0 R +>> +endobj + +157 0 obj +<< + /Type /StructElem + /S /H1 + /P 31 0 R + /T + /K [158 0 R] +>> +endobj + +158 0 obj +<< + /Type /StructElem + /S /P + /P 157 0 R + /K [0 1] + /Pg 786 0 R +>> +endobj + +159 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [161 0 R 160 0 R] +>> +endobj + +160 0 obj +<< + /Type /StructElem + /S /P + /P 159 0 R + /K [361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378] + /Pg 784 0 R +>> +endobj + +161 0 obj +<< + /Type /StructElem + /S /P + /P 159 0 R + /K [353 354 355 356 357 358 359 360] + /Pg 784 0 R +>> +endobj + +162 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [163 0 R 352] + /Pg 784 0 R +>> +endobj + +163 0 obj +<< + /Type /StructElem + /S /Strong + /P 162 0 R + /K [351] + /Pg 784 0 R +>> +endobj + +164 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [175 0 R 174 0 R 173 0 R 172 0 R 171 0 R 170 0 R 169 0 R 168 0 R 167 0 R 166 0 R 165 0 R] +>> +endobj + +165 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [347 348 349 350] + /Pg 784 0 R +>> +endobj + +166 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [336 337 338 339 340 341 342 343 344 345 346] + /Pg 784 0 R +>> +endobj + +167 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [328 329 330 331 332 333 334 335] + /Pg 784 0 R +>> +endobj + +168 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [319 320 321 322 323 324 325 326 327] + /Pg 784 0 R +>> +endobj + +169 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [304 305 306 307 308 309 310 311 312 313 314 315 316 317 318] + /Pg 784 0 R +>> +endobj + +170 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [298 299 300 301 302 303] + /Pg 784 0 R +>> +endobj + +171 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [] +>> +endobj + +172 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [286 287 288 289 290 291 292 293 294 295 296 297] + /Pg 784 0 R +>> +endobj + +173 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [278 279 280 281 282 283 284 285] + /Pg 784 0 R +>> +endobj + +174 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277] + /Pg 784 0 R +>> +endobj + +175 0 obj +<< + /Type /StructElem + /S /P + /P 164 0 R + /K [254 255 256 257 258 259] + /Pg 784 0 R +>> +endobj + +176 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [178 0 R 251 177 0 R 253] + /Pg 784 0 R +>> +endobj + +177 0 obj +<< + /Type /StructElem + /S /Code + /P 176 0 R + /K [252] + /Pg 784 0 R +>> +endobj + +178 0 obj +<< + /Type /StructElem + /S /Strong + /P 176 0 R + /K [250] + /Pg 784 0 R +>> +endobj + +179 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [182 0 R 237 238 239 240 181 0 R 242 243 244 180 0 R 246 247 248 249] + /Pg 784 0 R +>> +endobj + +180 0 obj +<< + /Type /StructElem + /S /Code + /P 179 0 R + /K [245] + /Pg 784 0 R +>> +endobj + +181 0 obj +<< + /Type /StructElem + /S /Code + /P 179 0 R + /K [241] + /Pg 784 0 R +>> +endobj + +182 0 obj +<< + /Type /StructElem + /S /Strong + /P 179 0 R + /K [236] + /Pg 784 0 R +>> +endobj + +183 0 obj +<< + /Type /StructElem + /S /H2 + /P 31 0 R + /T + /K [184 0 R] +>> +endobj + +184 0 obj +<< + /Type /StructElem + /S /P + /P 183 0 R + /K [231 232 233 234 235] + /Pg 784 0 R +>> +endobj + +185 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [187 0 R 186 0 R] +>> +endobj + +186 0 obj +<< + /Type /StructElem + /S /P + /P 185 0 R + /K [218 219 220 221 222 223 224 225 226 227 228 229 230] + /Pg 784 0 R +>> +endobj + +187 0 obj +<< + /Type /StructElem + /S /P + /P 185 0 R + /K [210 211 212 213 214 215 216 217] + /Pg 784 0 R +>> +endobj + +188 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [189 0 R 209] + /Pg 784 0 R +>> +endobj + +189 0 obj +<< + /Type /StructElem + /S /Strong + /P 188 0 R + /K [208] + /Pg 784 0 R +>> +endobj + +190 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [212 0 R 211 0 R 210 0 R 209 0 R 208 0 R 207 0 R 206 0 R 205 0 R 204 0 R 203 0 R 202 0 R 201 0 R 200 0 R 199 0 R 198 0 R 197 0 R 196 0 R 195 0 R 194 0 R 193 0 R 192 0 R 191 0 R] +>> +endobj + +191 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [195 196 197 198 199 200 201 202 203 204 205 206 207] + /Pg 784 0 R +>> +endobj + +192 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [194] + /Pg 784 0 R +>> +endobj + +193 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [185 186 187 188 189 190 191 192 193] + /Pg 784 0 R +>> +endobj + +194 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [184] + /Pg 784 0 R +>> +endobj + +195 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [172 173 174 175 176 177 178 179 180 181 182 183] + /Pg 784 0 R +>> +endobj + +196 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171] + /Pg 784 0 R +>> +endobj + +197 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151] + /Pg 784 0 R +>> +endobj + +198 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [115 116 117 118 119 120 121 122 123 124 125 126 127 128 129] + /Pg 784 0 R +>> +endobj + +199 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [103 104 105 106 107 108 109 110 111 112 113 114] + /Pg 784 0 R +>> +endobj + +200 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [97 98 99 100 101 102] + /Pg 784 0 R +>> +endobj + +201 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [96] + /Pg 784 0 R +>> +endobj + +202 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [84 85 86 87 88 89 90 91 92 93 94 95] + /Pg 784 0 R +>> +endobj + +203 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [70 71 72 73 74 75 76 77 78 79 80 81 82 83] + /Pg 784 0 R +>> +endobj + +204 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [61 62 63 64 65 66 67 68 69] + /Pg 784 0 R +>> +endobj + +205 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [55 56 57 58 59 60] + /Pg 784 0 R +>> +endobj + +206 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [54] + /Pg 784 0 R +>> +endobj + +207 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [50 51 52 53] + /Pg 784 0 R +>> +endobj + +208 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [43 44 45 46 47 48 49] + /Pg 784 0 R +>> +endobj + +209 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [42] + /Pg 784 0 R +>> +endobj + +210 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [32 33 34 35 36 37 38 39 40 41] + /Pg 784 0 R +>> +endobj + +211 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [24 25 26 27 28 29 30 31] + /Pg 784 0 R +>> +endobj + +212 0 obj +<< + /Type /StructElem + /S /P + /P 190 0 R + /K [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23] + /Pg 784 0 R +>> +endobj + +213 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [215 0 R 309 214 0 R 311] + /Pg 782 0 R +>> +endobj + +214 0 obj +<< + /Type /StructElem + /S /Code + /P 213 0 R + /K [310] + /Pg 782 0 R +>> +endobj + +215 0 obj +<< + /Type /StructElem + /S /Strong + /P 213 0 R + /K [308] + /Pg 782 0 R +>> +endobj + +216 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [217 0 R 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307] + /Pg 782 0 R +>> +endobj + +217 0 obj +<< + /Type /StructElem + /S /Strong + /P 216 0 R + /K [286] + /Pg 782 0 R +>> +endobj + +218 0 obj +<< + /Type /StructElem + /S /H2 + /P 31 0 R + /T + /K [219 0 R] +>> +endobj + +219 0 obj +<< + /Type /StructElem + /S /P + /P 218 0 R + /K [281 282 283 284 285] + /Pg 782 0 R +>> +endobj + +220 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [222 0 R 221 0 R] +>> +endobj + +221 0 obj +<< + /Type /StructElem + /S /P + /P 220 0 R + /K [263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280] + /Pg 782 0 R +>> +endobj + +222 0 obj +<< + /Type /StructElem + /S /P + /P 220 0 R + /K [255 256 257 258 259 260 261 262] + /Pg 782 0 R +>> +endobj + +223 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [224 0 R 254] + /Pg 782 0 R +>> +endobj + +224 0 obj +<< + /Type /StructElem + /S /Strong + /P 223 0 R + /K [253] + /Pg 782 0 R +>> +endobj + +225 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [252 0 R 251 0 R 250 0 R 249 0 R 248 0 R 247 0 R 246 0 R 245 0 R 244 0 R 243 0 R 242 0 R 241 0 R 240 0 R 239 0 R 238 0 R 237 0 R 236 0 R 235 0 R 234 0 R 233 0 R 232 0 R 231 0 R 230 0 R 229 0 R 228 0 R 227 0 R 226 0 R] +>> +endobj + +226 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [243 244 245 246 247 248 249 250 251 252] + /Pg 782 0 R +>> +endobj + +227 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [242] + /Pg 782 0 R +>> +endobj + +228 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [230 231 232 233 234 235 236 237 238 239 240 241] + /Pg 782 0 R +>> +endobj + +229 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229] + /Pg 782 0 R +>> +endobj + +230 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209] + /Pg 782 0 R +>> +endobj + +231 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [173 174 175 176 177 178 179 180 181 182 183 184 185 186 187] + /Pg 782 0 R +>> +endobj + +232 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [167 168 169 170 171 172] + /Pg 782 0 R +>> +endobj + +233 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [166] + /Pg 782 0 R +>> +endobj + +234 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [154 155 156 157 158 159 160 161 162 163 164 165] + /Pg 782 0 R +>> +endobj + +235 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [140 141 142 143 144 145 146 147 148 149 150 151 152 153] + /Pg 782 0 R +>> +endobj + +236 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [131 132 133 134 135 136 137 138 139] + /Pg 782 0 R +>> +endobj + +237 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [125 126 127 128 129 130] + /Pg 782 0 R +>> +endobj + +238 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [124] + /Pg 782 0 R +>> +endobj + +239 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [120 121 122 123] + /Pg 782 0 R +>> +endobj + +240 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [113 114 115 116 117 118 119] + /Pg 782 0 R +>> +endobj + +241 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [112] + /Pg 782 0 R +>> +endobj + +242 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [105 106 107 108 109 110 111] + /Pg 782 0 R +>> +endobj + +243 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [95 96 97 98 99 100 101 102 103 104] + /Pg 782 0 R +>> +endobj + +244 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [80 81 82 83 84 85 86 87 88 89 90 91 92 93 94] + /Pg 782 0 R +>> +endobj + +245 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [73 74 75 76 77 78 79] + /Pg 782 0 R +>> +endobj + +246 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [72] + /Pg 782 0 R +>> +endobj + +247 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [68 69 70 71] + /Pg 782 0 R +>> +endobj + +248 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [60 61 62 63 64 65 66 67] + /Pg 782 0 R +>> +endobj + +249 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [59] + /Pg 782 0 R +>> +endobj + +250 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [51 52 53 54 55 56 57 58] + /Pg 782 0 R +>> +endobj + +251 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [43 44 45 46 47 48 49 50] + /Pg 782 0 R +>> +endobj + +252 0 obj +<< + /Type /StructElem + /S /P + /P 225 0 R + /K [27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42] + /Pg 782 0 R +>> +endobj + +253 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [255 0 R 24 254 0 R 26] + /Pg 782 0 R +>> +endobj + +254 0 obj +<< + /Type /StructElem + /S /Code + /P 253 0 R + /K [25] + /Pg 782 0 R +>> +endobj + +255 0 obj +<< + /Type /StructElem + /S /Strong + /P 253 0 R + /K [23] + /Pg 782 0 R +>> +endobj + +256 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [257 0 R 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22] + /Pg 782 0 R +>> +endobj + +257 0 obj +<< + /Type /StructElem + /S /Strong + /P 256 0 R + /K [5] + /Pg 782 0 R +>> +endobj + +258 0 obj +<< + /Type /StructElem + /S /H2 + /P 31 0 R + /T + /K [259 0 R] +>> +endobj + +259 0 obj +<< + /Type /StructElem + /S /P + /P 258 0 R + /K [0 1 2 3 4] + /Pg 782 0 R +>> +endobj + +260 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [262 0 R 261 0 R] +>> +endobj + +261 0 obj +<< + /Type /StructElem + /S /P + /P 260 0 R + /K [399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416] + /Pg 780 0 R +>> +endobj + +262 0 obj +<< + /Type /StructElem + /S /P + /P 260 0 R + /K [391 392 393 394 395 396 397 398] + /Pg 780 0 R +>> +endobj + +263 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [264 0 R 390] + /Pg 780 0 R +>> +endobj + +264 0 obj +<< + /Type /StructElem + /S /Strong + /P 263 0 R + /K [389] + /Pg 780 0 R +>> +endobj + +265 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [299 0 R 298 0 R 297 0 R 296 0 R 295 0 R 294 0 R 293 0 R 292 0 R 291 0 R 290 0 R 289 0 R 288 0 R 287 0 R 286 0 R 285 0 R 284 0 R 283 0 R 282 0 R 281 0 R 280 0 R 279 0 R 278 0 R 277 0 R 276 0 R 275 0 R 274 0 R 273 0 R 272 0 R 271 0 R 270 0 R 269 0 R 268 0 R 267 0 R 266 0 R] +>> +endobj + +266 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [385 386 387 388] + /Pg 780 0 R +>> +endobj + +267 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [377 378 379 380 381 382 383 384] + /Pg 780 0 R +>> +endobj + +268 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [] +>> +endobj + +269 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [362 363 364 365 366 367 368 369 370 371 372 373 374 375 376] + /Pg 780 0 R +>> +endobj + +270 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [348 349 350 351 352 353 354 355 356 357 358 359 360 361] + /Pg 780 0 R +>> +endobj + +271 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [347] + /Pg 780 0 R +>> +endobj + +272 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [338 339 340 341 342 343 344 345 346] + /Pg 780 0 R +>> +endobj + +273 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337] + /Pg 780 0 R +>> +endobj + +274 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319] + /Pg 780 0 R +>> +endobj + +275 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [302] + /Pg 780 0 R +>> +endobj + +276 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [293 294 295 296 297 298 299 300 301] + /Pg 780 0 R +>> +endobj + +277 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [282 283 284 285 286 287 288 289 290 291 292] + /Pg 780 0 R +>> +endobj + +278 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [267 268 269 270 271 272 273 274 275 276 277 278 279 280 281] + /Pg 780 0 R +>> +endobj + +279 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [266] + /Pg 780 0 R +>> +endobj + +280 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265] + /Pg 780 0 R +>> +endobj + +281 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [229 230 231 232 233 234 235 236 237 238 239 240 241] + /Pg 780 0 R +>> +endobj + +282 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [221 222 223 224 225 226 227 228] + /Pg 780 0 R +>> +endobj + +283 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220] + /Pg 780 0 R +>> +endobj + +284 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [192] + /Pg 780 0 R +>> +endobj + +285 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [184 185 186 187 188 189 190 191] + /Pg 780 0 R +>> +endobj + +286 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [177 178 179 180 181 182 183] + /Pg 780 0 R +>> +endobj + +287 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [164 165 166 167 168 169 170 171 172 173 174 175 176] + /Pg 780 0 R +>> +endobj + +288 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [155 156 157 158 159 160 161 162 163] + /Pg 780 0 R +>> +endobj + +289 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [] +>> +endobj + +290 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [148 149 150 151 152 153 154] + /Pg 780 0 R +>> +endobj + +291 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [140 141 142 143 144 145 146 147] + /Pg 780 0 R +>> +endobj + +292 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [127 128 129 130 131 132 133 134 135 136 137 138 139] + /Pg 780 0 R +>> +endobj + +293 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [120 121 122 123 124 125 126] + /Pg 780 0 R +>> +endobj + +294 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [] +>> +endobj + +295 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [109 110 111 112 113 114 115 116 117 118 119] + /Pg 780 0 R +>> +endobj + +296 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108] + /Pg 780 0 R +>> +endobj + +297 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [75 76 77 78 79 80 81] + /Pg 780 0 R +>> +endobj + +298 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [71 72 73 74] + /Pg 780 0 R +>> +endobj + +299 0 obj +<< + /Type /StructElem + /S /P + /P 265 0 R + /K [60 61 62 63 64 65 66 67 68 69 70] + /Pg 780 0 R +>> +endobj + +300 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [302 0 R 57 301 0 R 59] + /Pg 780 0 R +>> +endobj + +301 0 obj +<< + /Type /StructElem + /S /Code + /P 300 0 R + /K [58] + /Pg 780 0 R +>> +endobj + +302 0 obj +<< + /Type /StructElem + /S /Strong + /P 300 0 R + /K [56] + /Pg 780 0 R +>> +endobj + +303 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [308 0 R 37 38 39 40 41 307 0 R 43 44 306 0 R 46 47 48 305 0 R 50 51 304 0 R 53 54 55] + /Pg 780 0 R +>> +endobj + +304 0 obj +<< + /Type /StructElem + /S /Code + /P 303 0 R + /K [52] + /Pg 780 0 R +>> +endobj + +305 0 obj +<< + /Type /StructElem + /S /Code + /P 303 0 R + /K [49] + /Pg 780 0 R +>> +endobj + +306 0 obj +<< + /Type /StructElem + /S /Code + /P 303 0 R + /K [45] + /Pg 780 0 R +>> +endobj + +307 0 obj +<< + /Type /StructElem + /S /Code + /P 303 0 R + /K [42] + /Pg 780 0 R +>> +endobj + +308 0 obj +<< + /Type /StructElem + /S /Strong + /P 303 0 R + /K [36] + /Pg 780 0 R +>> +endobj + +309 0 obj +<< + /Type /StructElem + /S /H2 + /P 31 0 R + /T + /K [310 0 R] +>> +endobj + +310 0 obj +<< + /Type /StructElem + /S /P + /P 309 0 R + /K [31 32 33 34 35] + /Pg 780 0 R +>> +endobj + +311 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [313 0 R 312 0 R] +>> +endobj + +312 0 obj +<< + /Type /StructElem + /S /P + /P 311 0 R + /K [8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30] + /Pg 780 0 R +>> +endobj + +313 0 obj +<< + /Type /StructElem + /S /P + /P 311 0 R + /K [0 1 2 3 4 5 6 7] + /Pg 780 0 R +>> +endobj + +314 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [315 0 R 391] + /Pg 778 0 R +>> +endobj + +315 0 obj +<< + /Type /StructElem + /S /Strong + /P 314 0 R + /K [390] + /Pg 778 0 R +>> +endobj + +316 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [344 0 R 343 0 R 342 0 R 341 0 R 340 0 R 339 0 R 338 0 R 337 0 R 336 0 R 335 0 R 334 0 R 333 0 R 332 0 R 331 0 R 330 0 R 329 0 R 328 0 R 327 0 R 326 0 R 325 0 R 324 0 R 323 0 R 322 0 R 321 0 R 320 0 R 319 0 R 318 0 R 317 0 R] +>> +endobj + +317 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [385 386 387 388 389] + /Pg 778 0 R +>> +endobj + +318 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [384] + /Pg 778 0 R +>> +endobj + +319 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383] + /Pg 778 0 R +>> +endobj + +320 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [354 355 356 357 358 359 360 361 362 363 364 365] + /Pg 778 0 R +>> +endobj + +321 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [344 345 346 347 348 349 350 351 352 353] + /Pg 778 0 R +>> +endobj + +322 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [332 333 334 335 336 337 338 339 340 341 342 343] + /Pg 778 0 R +>> +endobj + +323 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [322 323 324 325 326 327 328 329 330 331] + /Pg 778 0 R +>> +endobj + +324 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [307 308 309 310 311 312 313 314 315 316 317 318 319 320 321] + /Pg 778 0 R +>> +endobj + +325 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [306] + /Pg 778 0 R +>> +endobj + +326 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [295 296 297 298 299 300 301 302 303 304 305] + /Pg 778 0 R +>> +endobj + +327 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [294] + /Pg 778 0 R +>> +endobj + +328 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [290 291 292 293] + /Pg 778 0 R +>> +endobj + +329 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [280 281 282 283 284 285 286 287 288 289] + /Pg 778 0 R +>> +endobj + +330 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [279] + /Pg 778 0 R +>> +endobj + +331 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [270 271 272 273 274 275 276 277 278] + /Pg 778 0 R +>> +endobj + +332 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [269] + /Pg 778 0 R +>> +endobj + +333 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [267 268] + /Pg 778 0 R +>> +endobj + +334 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266] + /Pg 778 0 R +>> +endobj + +335 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [246] + /Pg 778 0 R +>> +endobj + +336 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [232 233 234 235 236 237 238 239 240 241 242 243 244 245] + /Pg 778 0 R +>> +endobj + +337 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [221 222 223 224 225 226 227 228 229 230 231] + /Pg 778 0 R +>> +endobj + +338 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [220] + /Pg 778 0 R +>> +endobj + +339 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219] + /Pg 778 0 R +>> +endobj + +340 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [189 190 191 192 193 194 195 196 197 198 199 200] + /Pg 778 0 R +>> +endobj + +341 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [179 180 181 182 183 184 185 186 187 188] + /Pg 778 0 R +>> +endobj + +342 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [172 173 174 175 176 177 178] + /Pg 778 0 R +>> +endobj + +343 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [162 163 164 165 166 167 168 169 170 171] + /Pg 778 0 R +>> +endobj + +344 0 obj +<< + /Type /StructElem + /S /P + /P 316 0 R + /K [147 148 149 150 151 152 153 154 155 156 157 158 159 160 161] + /Pg 778 0 R +>> +endobj + +345 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [347 0 R 144 346 0 R 146] + /Pg 778 0 R +>> +endobj + +346 0 obj +<< + /Type /StructElem + /S /Code + /P 345 0 R + /K [145] + /Pg 778 0 R +>> +endobj + +347 0 obj +<< + /Type /StructElem + /S /Strong + /P 345 0 R + /K [143] + /Pg 778 0 R +>> +endobj + +348 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [352 0 R 110 111 112 113 114 351 0 R 119 120 350 0 R 125 349 0 R 140 141 142] + /Pg 778 0 R +>> +endobj + +349 0 obj +<< + /Type /StructElem + /S /Formula + /P 348 0 R + /K [126 127 128 129 130 131 132 133 134 135 136 137 138 139] + /Pg 778 0 R +>> +endobj + +350 0 obj +<< + /Type /StructElem + /S /Formula + /P 348 0 R + /K [121 122 123 124] + /Pg 778 0 R +>> +endobj + +351 0 obj +<< + /Type /StructElem + /S /Formula + /P 348 0 R + /K [115 116 117 118] + /Pg 778 0 R +>> +endobj + +352 0 obj +<< + /Type /StructElem + /S /Strong + /P 348 0 R + /K [109] + /Pg 778 0 R +>> +endobj + +353 0 obj +<< + /Type /StructElem + /S /H2 + /P 31 0 R + /T + /K [354 0 R] +>> +endobj + +354 0 obj +<< + /Type /StructElem + /S /P + /P 353 0 R + /K [106 107 108] + /Pg 778 0 R +>> +endobj + +355 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [357 0 R 356 0 R] +>> +endobj + +356 0 obj +<< + /Type /StructElem + /S /P + /P 355 0 R + /K [88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105] + /Pg 778 0 R +>> +endobj + +357 0 obj +<< + /Type /StructElem + /S /P + /P 355 0 R + /K [80 81 82 83 84 85 86 87] + /Pg 778 0 R +>> +endobj + +358 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [359 0 R 79] + /Pg 778 0 R +>> +endobj + +359 0 obj +<< + /Type /StructElem + /S /Strong + /P 358 0 R + /K [78] + /Pg 778 0 R +>> +endobj + +360 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [385 0 R 384 0 R 383 0 R 382 0 R 381 0 R 380 0 R 379 0 R 378 0 R 377 0 R 376 0 R 375 0 R 374 0 R 373 0 R 372 0 R 371 0 R 370 0 R 369 0 R 368 0 R 367 0 R 366 0 R 365 0 R 364 0 R 363 0 R 362 0 R 361 0 R] +>> +endobj + +361 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [73 74 75 76 77] + /Pg 778 0 R +>> +endobj + +362 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [72] + /Pg 778 0 R +>> +endobj + +363 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71] + /Pg 778 0 R +>> +endobj + +364 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [42 43 44 45 46 47 48 49 50 51 52 53] + /Pg 778 0 R +>> +endobj + +365 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [32 33 34 35 36 37 38 39 40 41] + /Pg 778 0 R +>> +endobj + +366 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31] + /Pg 778 0 R +>> +endobj + +367 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [16] + /Pg 778 0 R +>> +endobj + +368 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [5 6 7 8 9 10 11 12 13 14 15] + /Pg 778 0 R +>> +endobj + +369 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [4] + /Pg 778 0 R +>> +endobj + +370 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [0 1 2 3] + /Pg 778 0 R +>> +endobj + +371 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [342 343 344 345 346 347 348 349 350 351] + /Pg 776 0 R +>> +endobj + +372 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [341] + /Pg 776 0 R +>> +endobj + +373 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [332 333 334 335 336 337 338 339 340] + /Pg 776 0 R +>> +endobj + +374 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [331] + /Pg 776 0 R +>> +endobj + +375 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [329 330] + /Pg 776 0 R +>> +endobj + +376 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328] + /Pg 776 0 R +>> +endobj + +377 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [308] + /Pg 776 0 R +>> +endobj + +378 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [294 295 296 297 298 299 300 301 302 303 304 305 306 307] + /Pg 776 0 R +>> +endobj + +379 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [283 284 285 286 287 288 289 290 291 292 293] + /Pg 776 0 R +>> +endobj + +380 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [282] + /Pg 776 0 R +>> +endobj + +381 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281] + /Pg 776 0 R +>> +endobj + +382 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [253 254 255 256 257 258 259 260 261 262] + /Pg 776 0 R +>> +endobj + +383 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [246 247 248 249 250 251 252] + /Pg 776 0 R +>> +endobj + +384 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [236 237 238 239 240 241 242 243 244 245] + /Pg 776 0 R +>> +endobj + +385 0 obj +<< + /Type /StructElem + /S /P + /P 360 0 R + /K [226 227 228 229 230 231 232 233 234 235] + /Pg 776 0 R +>> +endobj + +386 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [388 0 R 223 387 0 R 225] + /Pg 776 0 R +>> +endobj + +387 0 obj +<< + /Type /StructElem + /S /Code + /P 386 0 R + /K [224] + /Pg 776 0 R +>> +endobj + +388 0 obj +<< + /Type /StructElem + /S /Strong + /P 386 0 R + /K [222] + /Pg 776 0 R +>> +endobj + +389 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [390 0 R 213 214 215 216 217 218 219 220 221] + /Pg 776 0 R +>> +endobj + +390 0 obj +<< + /Type /StructElem + /S /Strong + /P 389 0 R + /K [212] + /Pg 776 0 R +>> +endobj + +391 0 obj +<< + /Type /StructElem + /S /H2 + /P 31 0 R + /T + /K [392 0 R] +>> +endobj + +392 0 obj +<< + /Type /StructElem + /S /P + /P 391 0 R + /K [207 208 209 210 211] + /Pg 776 0 R +>> +endobj + +393 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [395 0 R 394 0 R] +>> +endobj + +394 0 obj +<< + /Type /StructElem + /S /P + /P 393 0 R + /K [189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206] + /Pg 776 0 R +>> +endobj + +395 0 obj +<< + /Type /StructElem + /S /P + /P 393 0 R + /K [181 182 183 184 185 186 187 188] + /Pg 776 0 R +>> +endobj + +396 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [397 0 R 180] + /Pg 776 0 R +>> +endobj + +397 0 obj +<< + /Type /StructElem + /S /Strong + /P 396 0 R + /K [179] + /Pg 776 0 R +>> +endobj + +398 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [423 0 R 422 0 R 421 0 R 420 0 R 419 0 R 418 0 R 417 0 R 416 0 R 415 0 R 414 0 R 413 0 R 412 0 R 411 0 R 410 0 R 409 0 R 408 0 R 407 0 R 406 0 R 405 0 R 404 0 R 403 0 R 402 0 R 401 0 R 400 0 R 399 0 R] +>> +endobj + +399 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [174 175 176 177 178] + /Pg 776 0 R +>> +endobj + +400 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [173] + /Pg 776 0 R +>> +endobj + +401 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [161 162 163 164 165 166 167 168 169 170 171 172] + /Pg 776 0 R +>> +endobj + +402 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [149 150 151 152 153 154 155 156 157 158 159 160] + /Pg 776 0 R +>> +endobj + +403 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [138 139 140 141 142 143 144 145 146 147 148] + /Pg 776 0 R +>> +endobj + +404 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [123 124 125 126 127 128 129 130 131 132 133 134 135 136 137] + /Pg 776 0 R +>> +endobj + +405 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [122] + /Pg 776 0 R +>> +endobj + +406 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [111 112 113 114 115 116 117 118 119 120 121] + /Pg 776 0 R +>> +endobj + +407 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [110] + /Pg 776 0 R +>> +endobj + +408 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [106 107 108 109] + /Pg 776 0 R +>> +endobj + +409 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [96 97 98 99 100 101 102 103 104 105] + /Pg 776 0 R +>> +endobj + +410 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [95] + /Pg 776 0 R +>> +endobj + +411 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [88 89 90 91 92 93 94] + /Pg 776 0 R +>> +endobj + +412 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [87] + /Pg 776 0 R +>> +endobj + +413 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [85 86] + /Pg 776 0 R +>> +endobj + +414 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [76 77 78 79 80 81 82 83 84] + /Pg 776 0 R +>> +endobj + +415 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [75] + /Pg 776 0 R +>> +endobj + +416 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [63 64 65 66 67 68 69 70 71 72 73 74] + /Pg 776 0 R +>> +endobj + +417 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [52 53 54 55 56 57 58 59 60 61 62] + /Pg 776 0 R +>> +endobj + +418 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [51] + /Pg 776 0 R +>> +endobj + +419 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [38 39 40 41 42 43 44 45 46 47 48 49 50] + /Pg 776 0 R +>> +endobj + +420 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [28 29 30 31 32 33 34 35 36 37] + /Pg 776 0 R +>> +endobj + +421 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [20 21 22 23 24 25 26 27] + /Pg 776 0 R +>> +endobj + +422 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [10 11 12 13 14 15 16 17 18 19] + /Pg 776 0 R +>> +endobj + +423 0 obj +<< + /Type /StructElem + /S /P + /P 398 0 R + /K [0 1 2 3 4 5 6 7 8 9] + /Pg 776 0 R +>> +endobj + +424 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [426 0 R 272 425 0 R 274] + /Pg 774 0 R +>> +endobj + +425 0 obj +<< + /Type /StructElem + /S /Code + /P 424 0 R + /K [273] + /Pg 774 0 R +>> +endobj + +426 0 obj +<< + /Type /StructElem + /S /Strong + /P 424 0 R + /K [271] + /Pg 774 0 R +>> +endobj + +427 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [428 0 R 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270] + /Pg 774 0 R +>> +endobj + +428 0 obj +<< + /Type /StructElem + /S /Strong + /P 427 0 R + /K [253] + /Pg 774 0 R +>> +endobj + +429 0 obj +<< + /Type /StructElem + /S /H2 + /P 31 0 R + /T + /K [430 0 R] +>> +endobj + +430 0 obj +<< + /Type /StructElem + /S /P + /P 429 0 R + /K [248 249 250 251 252] + /Pg 774 0 R +>> +endobj + +431 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [433 0 R 432 0 R] +>> +endobj + +432 0 obj +<< + /Type /StructElem + /S /P + /P 431 0 R + /K [230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247] + /Pg 774 0 R +>> +endobj + +433 0 obj +<< + /Type /StructElem + /S /P + /P 431 0 R + /K [222 223 224 225 226 227 228 229] + /Pg 774 0 R +>> +endobj + +434 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [435 0 R 221] + /Pg 774 0 R +>> +endobj + +435 0 obj +<< + /Type /StructElem + /S /Strong + /P 434 0 R + /K [220] + /Pg 774 0 R +>> +endobj + +436 0 obj +<< + /Type /StructElem + /S /Code + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [461 0 R 460 0 R 459 0 R 458 0 R 457 0 R 456 0 R 455 0 R 454 0 R 453 0 R 452 0 R 451 0 R 450 0 R 449 0 R 448 0 R 447 0 R 446 0 R 445 0 R 444 0 R 443 0 R 442 0 R 441 0 R 440 0 R 439 0 R 438 0 R 437 0 R] +>> +endobj + +437 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [215 216 217 218 219] + /Pg 774 0 R +>> +endobj + +438 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [214] + /Pg 774 0 R +>> +endobj + +439 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [202 203 204 205 206 207 208 209 210 211 212 213] + /Pg 774 0 R +>> +endobj + +440 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [190 191 192 193 194 195 196 197 198 199 200 201] + /Pg 774 0 R +>> +endobj + +441 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [179 180 181 182 183 184 185 186 187 188 189] + /Pg 774 0 R +>> +endobj + +442 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [164 165 166 167 168 169 170 171 172 173 174 175 176 177 178] + /Pg 774 0 R +>> +endobj + +443 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [163] + /Pg 774 0 R +>> +endobj + +444 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [152 153 154 155 156 157 158 159 160 161 162] + /Pg 774 0 R +>> +endobj + +445 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [151] + /Pg 774 0 R +>> +endobj + +446 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [147 148 149 150] + /Pg 774 0 R +>> +endobj + +447 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [137 138 139 140 141 142 143 144 145 146] + /Pg 774 0 R +>> +endobj + +448 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [136] + /Pg 774 0 R +>> +endobj + +449 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [129 130 131 132 133 134 135] + /Pg 774 0 R +>> +endobj + +450 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [128] + /Pg 774 0 R +>> +endobj + +451 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [126 127] + /Pg 774 0 R +>> +endobj + +452 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [117 118 119 120 121 122 123 124 125] + /Pg 774 0 R +>> +endobj + +453 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [116] + /Pg 774 0 R +>> +endobj + +454 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [104 105 106 107 108 109 110 111 112 113 114 115] + /Pg 774 0 R +>> +endobj + +455 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [93 94 95 96 97 98 99 100 101 102 103] + /Pg 774 0 R +>> +endobj + +456 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [92] + /Pg 774 0 R +>> +endobj + +457 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [79 80 81 82 83 84 85 86 87 88 89 90 91] + /Pg 774 0 R +>> +endobj + +458 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [69 70 71 72 73 74 75 76 77 78] + /Pg 774 0 R +>> +endobj + +459 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [61 62 63 64 65 66 67 68] + /Pg 774 0 R +>> +endobj + +460 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [51 52 53 54 55 56 57 58 59 60] + /Pg 774 0 R +>> +endobj + +461 0 obj +<< + /Type /StructElem + /S /P + /P 436 0 R + /K [41 42 43 44 45 46 47 48 49 50] + /Pg 774 0 R +>> +endobj + +462 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [464 0 R 38 463 0 R 40] + /Pg 774 0 R +>> +endobj + +463 0 obj +<< + /Type /StructElem + /S /Code + /P 462 0 R + /K [39] + /Pg 774 0 R +>> +endobj + +464 0 obj +<< + /Type /StructElem + /S /Strong + /P 462 0 R + /K [37] + /Pg 774 0 R +>> +endobj + +465 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [466 0 R 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36] + /Pg 774 0 R +>> +endobj + +466 0 obj +<< + /Type /StructElem + /S /Strong + /P 465 0 R + /K [18] + /Pg 774 0 R +>> +endobj + +467 0 obj +<< + /Type /StructElem + /S /H2 + /P 31 0 R + /T + /K [468 0 R] +>> +endobj + +468 0 obj +<< + /Type /StructElem + /S /P + /P 467 0 R + /K [13 14 15 16 17] + /Pg 774 0 R +>> +endobj + +469 0 obj +<< + /Type /StructElem + /S /H1 + /P 31 0 R + /T + /K [470 0 R] +>> +endobj + +470 0 obj +<< + /Type /StructElem + /S /P + /P 469 0 R + /K [11 12] + /Pg 774 0 R +>> +endobj + +471 0 obj +<< + /Type /StructElem + /S /L + /P 31 0 R + /A [<< + /O /List + /ListNumbering /Decimal + >>] + /K [498 0 R 490 0 R 484 0 R 478 0 R 472 0 R] +>> +endobj + +472 0 obj +<< + /Type /StructElem + /S /LI + /P 471 0 R + /K [477 0 R 473 0 R] +>> +endobj + +473 0 obj +<< + /Type /StructElem + /S /LBody + /P 472 0 R + /K [476 0 R 2 3 475 0 R 5 6 7 474 0 R 9 10] + /Pg 774 0 R +>> +endobj + +474 0 obj +<< + /Type /StructElem + /S /Code + /P 473 0 R + /K [8] + /Pg 774 0 R +>> +endobj + +475 0 obj +<< + /Type /StructElem + /S /Code + /P 473 0 R + /K [4] + /Pg 774 0 R +>> +endobj + +476 0 obj +<< + /Type /StructElem + /S /Strong + /P 473 0 R + /K [1] + /Pg 774 0 R +>> +endobj + +477 0 obj +<< + /Type /StructElem + /S /Lbl + /P 472 0 R + /K [0] + /Pg 774 0 R +>> +endobj + +478 0 obj +<< + /Type /StructElem + /S /LI + /P 471 0 R + /K [483 0 R 479 0 R] +>> +endobj + +479 0 obj +<< + /Type /StructElem + /S /LBody + /P 478 0 R + /K [482 0 R 145 481 0 R 147 148 149 480 0 R 151 152 153 154 155 156 157 158 159 160] + /Pg 772 0 R +>> +endobj + +480 0 obj +<< + /Type /StructElem + /S /Code + /P 479 0 R + /K [150] + /Pg 772 0 R +>> +endobj + +481 0 obj +<< + /Type /StructElem + /S /Code + /P 479 0 R + /K [146] + /Pg 772 0 R +>> +endobj + +482 0 obj +<< + /Type /StructElem + /S /Strong + /P 479 0 R + /K [144] + /Pg 772 0 R +>> +endobj + +483 0 obj +<< + /Type /StructElem + /S /Lbl + /P 478 0 R + /K [143] + /Pg 772 0 R +>> +endobj + +484 0 obj +<< + /Type /StructElem + /S /LI + /P 471 0 R + /K [489 0 R 485 0 R] +>> +endobj + +485 0 obj +<< + /Type /StructElem + /S /LBody + /P 484 0 R + /K [488 0 R 135 136 487 0 R 138 139 486 0 R 141 142] + /Pg 772 0 R +>> +endobj + +486 0 obj +<< + /Type /StructElem + /S /Code + /P 485 0 R + /K [140] + /Pg 772 0 R +>> +endobj + +487 0 obj +<< + /Type /StructElem + /S /Code + /P 485 0 R + /K [137] + /Pg 772 0 R +>> +endobj + +488 0 obj +<< + /Type /StructElem + /S /Strong + /P 485 0 R + /K [134] + /Pg 772 0 R +>> +endobj + +489 0 obj +<< + /Type /StructElem + /S /Lbl + /P 484 0 R + /K [133] + /Pg 772 0 R +>> +endobj + +490 0 obj +<< + /Type /StructElem + /S /LI + /P 471 0 R + /K [497 0 R 491 0 R] +>> +endobj + +491 0 obj +<< + /Type /StructElem + /S /LBody + /P 490 0 R + /K [496 0 R 117 118 495 0 R 120 121 122 494 0 R 124 493 0 R 126 127 128 492 0 R 130 131 132] + /Pg 772 0 R +>> +endobj + +492 0 obj +<< + /Type /StructElem + /S /Code + /P 491 0 R + /K [129] + /Pg 772 0 R +>> +endobj + +493 0 obj +<< + /Type /StructElem + /S /Code + /P 491 0 R + /K [125] + /Pg 772 0 R +>> +endobj + +494 0 obj +<< + /Type /StructElem + /S /Code + /P 491 0 R + /K [123] + /Pg 772 0 R +>> +endobj + +495 0 obj +<< + /Type /StructElem + /S /Code + /P 491 0 R + /K [119] + /Pg 772 0 R +>> +endobj + +496 0 obj +<< + /Type /StructElem + /S /Strong + /P 491 0 R + /K [116] + /Pg 772 0 R +>> +endobj + +497 0 obj +<< + /Type /StructElem + /S /Lbl + /P 490 0 R + /K [115] + /Pg 772 0 R +>> +endobj + +498 0 obj +<< + /Type /StructElem + /S /LI + /P 471 0 R + /K [506 0 R 499 0 R] +>> +endobj + +499 0 obj +<< + /Type /StructElem + /S /LBody + /P 498 0 R + /K [505 0 R 94 95 504 0 R 97 98 99 503 0 R 101 502 0 R 103 501 0 R 105 106 107 500 0 R 109 110 111 112 113 114] + /Pg 772 0 R +>> +endobj + +500 0 obj +<< + /Type /StructElem + /S /Code + /P 499 0 R + /K [108] + /Pg 772 0 R +>> +endobj + +501 0 obj +<< + /Type /StructElem + /S /Code + /P 499 0 R + /K [104] + /Pg 772 0 R +>> +endobj + +502 0 obj +<< + /Type /StructElem + /S /Code + /P 499 0 R + /K [102] + /Pg 772 0 R +>> +endobj + +503 0 obj +<< + /Type /StructElem + /S /Code + /P 499 0 R + /K [100] + /Pg 772 0 R +>> +endobj + +504 0 obj +<< + /Type /StructElem + /S /Code + /P 499 0 R + /K [96] + /Pg 772 0 R +>> +endobj + +505 0 obj +<< + /Type /StructElem + /S /Strong + /P 499 0 R + /K [93] + /Pg 772 0 R +>> +endobj + +506 0 obj +<< + /Type /StructElem + /S /Lbl + /P 498 0 R + /K [92] + /Pg 772 0 R +>> +endobj + +507 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [82 83 509 0 R 85 86 87 508 0 R 89 90 91] + /Pg 772 0 R +>> +endobj + +508 0 obj +<< + /Type /StructElem + /S /Code + /P 507 0 R + /K [88] + /Pg 772 0 R +>> +endobj + +509 0 obj +<< + /Type /StructElem + /S /Code + /P 507 0 R + /K [84] + /Pg 772 0 R +>> +endobj + +510 0 obj +<< + /Type /StructElem + /S /H1 + /P 31 0 R + /T + /K [511 0 R] +>> +endobj + +511 0 obj +<< + /Type /StructElem + /S /P + /P 510 0 R + /K [80 81] + /Pg 772 0 R +>> +endobj + +512 0 obj +<< + /Type /StructElem + /S /L + /P 31 0 R + /A [<< + /O /List + /ListNumbering /Decimal + >>] + /K [541 0 R 537 0 R 533 0 R 529 0 R 525 0 R 521 0 R 517 0 R 513 0 R] +>> +endobj + +513 0 obj +<< + /Type /StructElem + /S /LI + /P 512 0 R + /K [516 0 R 514 0 R] +>> +endobj + +514 0 obj +<< + /Type /StructElem + /S /LBody + /P 513 0 R + /K [515 0 R 78 79] + /Pg 772 0 R +>> +endobj + +515 0 obj +<< + /Type /StructElem + /S /Strong + /P 514 0 R + /K [76 77] + /Pg 772 0 R +>> +endobj + +516 0 obj +<< + /Type /StructElem + /S /Lbl + /P 513 0 R + /K [75] + /Pg 772 0 R +>> +endobj + +517 0 obj +<< + /Type /StructElem + /S /LI + /P 512 0 R + /K [520 0 R 518 0 R] +>> +endobj + +518 0 obj +<< + /Type /StructElem + /S /LBody + /P 517 0 R + /K [519 0 R 69 70 71 72 73 74] + /Pg 772 0 R +>> +endobj + +519 0 obj +<< + /Type /StructElem + /S /Strong + /P 518 0 R + /K [67 68] + /Pg 772 0 R +>> +endobj + +520 0 obj +<< + /Type /StructElem + /S /Lbl + /P 517 0 R + /K [66] + /Pg 772 0 R +>> +endobj + +521 0 obj +<< + /Type /StructElem + /S /LI + /P 512 0 R + /K [524 0 R 522 0 R] +>> +endobj + +522 0 obj +<< + /Type /StructElem + /S /LBody + /P 521 0 R + /K [523 0 R 62 63 64 65] + /Pg 772 0 R +>> +endobj + +523 0 obj +<< + /Type /StructElem + /S /Strong + /P 522 0 R + /K [60 61] + /Pg 772 0 R +>> +endobj + +524 0 obj +<< + /Type /StructElem + /S /Lbl + /P 521 0 R + /K [59] + /Pg 772 0 R +>> +endobj + +525 0 obj +<< + /Type /StructElem + /S /LI + /P 512 0 R + /K [528 0 R 526 0 R] +>> +endobj + +526 0 obj +<< + /Type /StructElem + /S /LBody + /P 525 0 R + /K [527 0 R 57 58] + /Pg 772 0 R +>> +endobj + +527 0 obj +<< + /Type /StructElem + /S /Strong + /P 526 0 R + /K [55 56] + /Pg 772 0 R +>> +endobj + +528 0 obj +<< + /Type /StructElem + /S /Lbl + /P 525 0 R + /K [54] + /Pg 772 0 R +>> +endobj + +529 0 obj +<< + /Type /StructElem + /S /LI + /P 512 0 R + /K [532 0 R 530 0 R] +>> +endobj + +530 0 obj +<< + /Type /StructElem + /S /LBody + /P 529 0 R + /K [531 0 R 50 51 52 53] + /Pg 772 0 R +>> +endobj + +531 0 obj +<< + /Type /StructElem + /S /Strong + /P 530 0 R + /K [48 49] + /Pg 772 0 R +>> +endobj + +532 0 obj +<< + /Type /StructElem + /S /Lbl + /P 529 0 R + /K [47] + /Pg 772 0 R +>> +endobj + +533 0 obj +<< + /Type /StructElem + /S /LI + /P 512 0 R + /K [536 0 R 534 0 R] +>> +endobj + +534 0 obj +<< + /Type /StructElem + /S /LBody + /P 533 0 R + /K [535 0 R 45 46] + /Pg 772 0 R +>> +endobj + +535 0 obj +<< + /Type /StructElem + /S /Strong + /P 534 0 R + /K [41 42 43 44] + /Pg 772 0 R +>> +endobj + +536 0 obj +<< + /Type /StructElem + /S /Lbl + /P 533 0 R + /K [40] + /Pg 772 0 R +>> +endobj + +537 0 obj +<< + /Type /StructElem + /S /LI + /P 512 0 R + /K [540 0 R 538 0 R] +>> +endobj + +538 0 obj +<< + /Type /StructElem + /S /LBody + /P 537 0 R + /K [539 0 R 38 39] + /Pg 772 0 R +>> +endobj + +539 0 obj +<< + /Type /StructElem + /S /Strong + /P 538 0 R + /K [34 35 36 37] + /Pg 772 0 R +>> +endobj + +540 0 obj +<< + /Type /StructElem + /S /Lbl + /P 537 0 R + /K [33] + /Pg 772 0 R +>> +endobj + +541 0 obj +<< + /Type /StructElem + /S /LI + /P 512 0 R + /K [544 0 R 542 0 R] +>> +endobj + +542 0 obj +<< + /Type /StructElem + /S /LBody + /P 541 0 R + /K [543 0 R 32] + /Pg 772 0 R +>> +endobj + +543 0 obj +<< + /Type /StructElem + /S /Strong + /P 542 0 R + /K [28 29 30 31] + /Pg 772 0 R +>> +endobj + +544 0 obj +<< + /Type /StructElem + /S /Lbl + /P 541 0 R + /K [27] + /Pg 772 0 R +>> +endobj + +545 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [24 25 26] + /Pg 772 0 R +>> +endobj + +546 0 obj +<< + /Type /StructElem + /S /H1 + /P 31 0 R + /T + /K [547 0 R] +>> +endobj + +547 0 obj +<< + /Type /StructElem + /S /P + /P 546 0 R + /K [22 23] + /Pg 772 0 R +>> +endobj + +548 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21] + /Pg 772 0 R +>> +endobj + +549 0 obj +<< + /Type /StructElem + /S /H1 + /P 31 0 R + /T + /K [550 0 R] +>> +endobj + +550 0 obj +<< + /Type /StructElem + /S /P + /P 549 0 R + /K [0 1] + /Pg 772 0 R +>> +endobj + +551 0 obj +<< + /Type /StructElem + /S /TOC + /P 31 0 R + /K [605 0 R 601 0 R 597 0 R 593 0 R 560 0 R 556 0 R 552 0 R] +>> +endobj + +552 0 obj +<< + /Type /StructElem + /S /TOCI + /P 551 0 R + /K [553 0 R] +>> +endobj + +553 0 obj +<< + /Type /StructElem + /S /Reference + /P 552 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [554 0 R] +>> +endobj + +554 0 obj +<< + /Type /StructElem + /S /Link + /P 553 0 R + /K [555 0 R 70 71 72 << + /Type /OBJR + /Pg 770 0 R + /Obj 769 0 R + >>] + /Pg 770 0 R +>> +endobj + +555 0 obj +<< + /Type /StructElem + /S /Lbl + /P 554 0 R + /K [69] + /Pg 770 0 R +>> +endobj + +556 0 obj +<< + /Type /StructElem + /S /TOCI + /P 551 0 R + /K [557 0 R] +>> +endobj + +557 0 obj +<< + /Type /StructElem + /S /Reference + /P 556 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [558 0 R] +>> +endobj + +558 0 obj +<< + /Type /StructElem + /S /Link + /P 557 0 R + /K [559 0 R 66 67 68 << + /Type /OBJR + /Pg 770 0 R + /Obj 768 0 R + >>] + /Pg 770 0 R +>> +endobj + +559 0 obj +<< + /Type /StructElem + /S /Lbl + /P 558 0 R + /K [65] + /Pg 770 0 R +>> +endobj + +560 0 obj +<< + /Type /StructElem + /S /TOC + /P 551 0 R + /K [589 0 R 585 0 R 581 0 R 577 0 R 573 0 R 569 0 R 565 0 R 561 0 R] +>> +endobj + +561 0 obj +<< + /Type /StructElem + /S /TOCI + /P 560 0 R + /K [562 0 R] +>> +endobj + +562 0 obj +<< + /Type /StructElem + /S /Reference + /P 561 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [563 0 R] +>> +endobj + +563 0 obj +<< + /Type /StructElem + /S /Link + /P 562 0 R + /K [564 0 R 60 61 62 63 64 << + /Type /OBJR + /Pg 770 0 R + /Obj 767 0 R + >>] + /Pg 770 0 R +>> +endobj + +564 0 obj +<< + /Type /StructElem + /S /Lbl + /P 563 0 R + /K [59] + /Pg 770 0 R +>> +endobj + +565 0 obj +<< + /Type /StructElem + /S /TOCI + /P 560 0 R + /K [566 0 R] +>> +endobj + +566 0 obj +<< + /Type /StructElem + /S /Reference + /P 565 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [567 0 R] +>> +endobj + +567 0 obj +<< + /Type /StructElem + /S /Link + /P 566 0 R + /K [568 0 R 54 55 56 57 58 << + /Type /OBJR + /Pg 770 0 R + /Obj 766 0 R + >>] + /Pg 770 0 R +>> +endobj + +568 0 obj +<< + /Type /StructElem + /S /Lbl + /P 567 0 R + /K [53] + /Pg 770 0 R +>> +endobj + +569 0 obj +<< + /Type /StructElem + /S /TOCI + /P 560 0 R + /K [570 0 R] +>> +endobj + +570 0 obj +<< + /Type /StructElem + /S /Reference + /P 569 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [571 0 R] +>> +endobj + +571 0 obj +<< + /Type /StructElem + /S /Link + /P 570 0 R + /K [572 0 R 48 49 50 51 52 << + /Type /OBJR + /Pg 770 0 R + /Obj 765 0 R + >>] + /Pg 770 0 R +>> +endobj + +572 0 obj +<< + /Type /StructElem + /S /Lbl + /P 571 0 R + /K [47] + /Pg 770 0 R +>> +endobj + +573 0 obj +<< + /Type /StructElem + /S /TOCI + /P 560 0 R + /K [574 0 R] +>> +endobj + +574 0 obj +<< + /Type /StructElem + /S /Reference + /P 573 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [575 0 R] +>> +endobj + +575 0 obj +<< + /Type /StructElem + /S /Link + /P 574 0 R + /K [576 0 R 42 43 44 45 46 << + /Type /OBJR + /Pg 770 0 R + /Obj 764 0 R + >>] + /Pg 770 0 R +>> +endobj + +576 0 obj +<< + /Type /StructElem + /S /Lbl + /P 575 0 R + /K [41] + /Pg 770 0 R +>> +endobj + +577 0 obj +<< + /Type /StructElem + /S /TOCI + /P 560 0 R + /K [578 0 R] +>> +endobj + +578 0 obj +<< + /Type /StructElem + /S /Reference + /P 577 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [579 0 R] +>> +endobj + +579 0 obj +<< + /Type /StructElem + /S /Link + /P 578 0 R + /K [580 0 R 37 38 39 40 << + /Type /OBJR + /Pg 770 0 R + /Obj 763 0 R + >>] + /Pg 770 0 R +>> +endobj + +580 0 obj +<< + /Type /StructElem + /S /Lbl + /P 579 0 R + /K [36] + /Pg 770 0 R +>> +endobj + +581 0 obj +<< + /Type /StructElem + /S /TOCI + /P 560 0 R + /K [582 0 R] +>> +endobj + +582 0 obj +<< + /Type /StructElem + /S /Reference + /P 581 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [583 0 R] +>> +endobj + +583 0 obj +<< + /Type /StructElem + /S /Link + /P 582 0 R + /K [584 0 R 31 32 33 34 35 << + /Type /OBJR + /Pg 770 0 R + /Obj 762 0 R + >>] + /Pg 770 0 R +>> +endobj + +584 0 obj +<< + /Type /StructElem + /S /Lbl + /P 583 0 R + /K [30] + /Pg 770 0 R +>> +endobj + +585 0 obj +<< + /Type /StructElem + /S /TOCI + /P 560 0 R + /K [586 0 R] +>> +endobj + +586 0 obj +<< + /Type /StructElem + /S /Reference + /P 585 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [587 0 R] +>> +endobj + +587 0 obj +<< + /Type /StructElem + /S /Link + /P 586 0 R + /K [588 0 R 25 26 27 28 29 << + /Type /OBJR + /Pg 770 0 R + /Obj 761 0 R + >>] + /Pg 770 0 R +>> +endobj + +588 0 obj +<< + /Type /StructElem + /S /Lbl + /P 587 0 R + /K [24] + /Pg 770 0 R +>> +endobj + +589 0 obj +<< + /Type /StructElem + /S /TOCI + /P 560 0 R + /K [590 0 R] +>> +endobj + +590 0 obj +<< + /Type /StructElem + /S /Reference + /P 589 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [591 0 R] +>> +endobj + +591 0 obj +<< + /Type /StructElem + /S /Link + /P 590 0 R + /K [592 0 R 19 20 21 22 23 << + /Type /OBJR + /Pg 770 0 R + /Obj 760 0 R + >>] + /Pg 770 0 R +>> +endobj + +592 0 obj +<< + /Type /StructElem + /S /Lbl + /P 591 0 R + /K [18] + /Pg 770 0 R +>> +endobj + +593 0 obj +<< + /Type /StructElem + /S /TOCI + /P 551 0 R + /K [594 0 R] +>> +endobj + +594 0 obj +<< + /Type /StructElem + /S /Reference + /P 593 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [595 0 R] +>> +endobj + +595 0 obj +<< + /Type /StructElem + /S /Link + /P 594 0 R + /K [596 0 R 15 16 17 << + /Type /OBJR + /Pg 770 0 R + /Obj 759 0 R + >>] + /Pg 770 0 R +>> +endobj + +596 0 obj +<< + /Type /StructElem + /S /Lbl + /P 595 0 R + /K [14] + /Pg 770 0 R +>> +endobj + +597 0 obj +<< + /Type /StructElem + /S /TOCI + /P 551 0 R + /K [598 0 R] +>> +endobj + +598 0 obj +<< + /Type /StructElem + /S /Reference + /P 597 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [599 0 R] +>> +endobj + +599 0 obj +<< + /Type /StructElem + /S /Link + /P 598 0 R + /K [600 0 R 11 12 13 << + /Type /OBJR + /Pg 770 0 R + /Obj 758 0 R + >>] + /Pg 770 0 R +>> +endobj + +600 0 obj +<< + /Type /StructElem + /S /Lbl + /P 599 0 R + /K [10] + /Pg 770 0 R +>> +endobj + +601 0 obj +<< + /Type /StructElem + /S /TOCI + /P 551 0 R + /K [602 0 R] +>> +endobj + +602 0 obj +<< + /Type /StructElem + /S /Reference + /P 601 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [603 0 R] +>> +endobj + +603 0 obj +<< + /Type /StructElem + /S /Link + /P 602 0 R + /K [604 0 R 7 8 9 << + /Type /OBJR + /Pg 770 0 R + /Obj 757 0 R + >>] + /Pg 770 0 R +>> +endobj + +604 0 obj +<< + /Type /StructElem + /S /Lbl + /P 603 0 R + /K [6] + /Pg 770 0 R +>> +endobj + +605 0 obj +<< + /Type /StructElem + /S /TOCI + /P 551 0 R + /K [606 0 R] +>> +endobj + +606 0 obj +<< + /Type /StructElem + /S /Reference + /P 605 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [607 0 R] +>> +endobj + +607 0 obj +<< + /Type /StructElem + /S /Link + /P 606 0 R + /K [608 0 R 3 4 5 << + /Type /OBJR + /Pg 770 0 R + /Obj 756 0 R + >>] + /Pg 770 0 R +>> +endobj + +608 0 obj +<< + /Type /StructElem + /S /Lbl + /P 607 0 R + /K [2] + /Pg 770 0 R +>> +endobj + +609 0 obj +<< + /Type /StructElem + /S /H1 + /P 31 0 R + /T + /K [610 0 R] +>> +endobj + +610 0 obj +<< + /Type /StructElem + /S /P + /P 609 0 R + /K [0 1] + /Pg 770 0 R +>> +endobj + +611 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [76 77 78 79 80] + /Pg 754 0 R +>> +endobj + +612 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75] + /Pg 754 0 R +>> +endobj + +613 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33] + /Pg 754 0 R +>> +endobj + +614 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [1] + /Pg 754 0 R +>> +endobj + +615 0 obj +<< + /Type /StructElem + /S /Span + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [0] + /Pg 754 0 R +>> +endobj + +616 0 obj +<< + /Type /StructElem + /S /P + /P 31 0 R + /K [11] + /Pg 752 0 R +>> +endobj + +617 0 obj +<< + /Type /StructElem + /S /Div + /P 31 0 R + /K [634 0 R 632 0 R 631 0 R 630 0 R 628 0 R 626 0 R 625 0 R 624 0 R 622 0 R 620 0 R 619 0 R 618 0 R] +>> +endobj + +618 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [] +>> +endobj + +619 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [] +>> +endobj + +620 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [621 0 R] +>> +endobj + +621 0 obj +<< + /Type /StructElem + /S /Span + /P 620 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [10] + /Pg 752 0 R +>> +endobj + +622 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [623 0 R] +>> +endobj + +623 0 obj +<< + /Type /StructElem + /S /Span + /P 622 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [9] + /Pg 752 0 R +>> +endobj + +624 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [] +>> +endobj + +625 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [] +>> +endobj + +626 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [627 0 R] +>> +endobj + +627 0 obj +<< + /Type /StructElem + /S /Span + /P 626 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [8] + /Pg 752 0 R +>> +endobj + +628 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [629 0 R] +>> +endobj + +629 0 obj +<< + /Type /StructElem + /S /Span + /P 628 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [5 6 7] + /Pg 752 0 R +>> +endobj + +630 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [] +>> +endobj + +631 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [] +>> +endobj + +632 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [633 0 R] +>> +endobj + +633 0 obj +<< + /Type /StructElem + /S /Span + /P 632 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [4] + /Pg 752 0 R +>> +endobj + +634 0 obj +<< + /Type /StructElem + /S /Div + /P 617 0 R + /K [635 0 R] +>> +endobj + +635 0 obj +<< + /Type /StructElem + /S /Span + /P 634 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [3] + /Pg 752 0 R +>> +endobj + +636 0 obj +<< + /Type /StructElem + /S /Div + /P 31 0 R + /K [641 0 R 639 0 R 638 0 R 637 0 R] +>> +endobj + +637 0 obj +<< + /Type /StructElem + /S /Div + /P 636 0 R + /K [] +>> +endobj + +638 0 obj +<< + /Type /StructElem + /S /Div + /P 636 0 R + /K [] +>> +endobj + +639 0 obj +<< + /Type /StructElem + /S /Div + /P 636 0 R + /K [640 0 R] +>> +endobj + +640 0 obj +<< + /Type /StructElem + /S /Span + /P 639 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [2] + /Pg 752 0 R +>> +endobj + +641 0 obj +<< + /Type /StructElem + /S /Div + /P 636 0 R + /K [642 0 R] +>> +endobj + +642 0 obj +<< + /Type /StructElem + /S /Span + /P 641 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [1] + /Pg 752 0 R +>> +endobj + +643 0 obj +<< + /Type /StructElem + /S /Span + /P 31 0 R + /A [<< + /O /Layout + /Placement /Block + >>] + /K [0] + /Pg 752 0 R +>> +endobj + +644 0 obj +<< + /Type /PageLabel +>> +endobj + +645 0 obj +<< + /Type /PageLabel + /S /r + /St 2 +>> +endobj + +646 0 obj +<< + /Type /PageLabel + /S /D + /St 3 +>> +endobj + +647 0 obj +<< + /Type /PageLabel + /S /D + /St 4 +>> +endobj + +648 0 obj +<< + /Type /PageLabel + /S /D + /St 5 +>> +endobj + +649 0 obj +<< + /Type /PageLabel + /S /D + /St 6 +>> +endobj + +650 0 obj +<< + /Type /PageLabel + /S /D + /St 7 +>> +endobj + +651 0 obj +<< + /Type /PageLabel + /S /D + /St 8 +>> +endobj + +652 0 obj +<< + /Type /PageLabel + /S /D + /St 9 +>> +endobj + +653 0 obj +<< + /Type /PageLabel + /S /D + /St 10 +>> +endobj + +654 0 obj +<< + /Type /PageLabel + /S /D + /St 11 +>> +endobj + +655 0 obj +<< + /Type /PageLabel + /S /D + /St 12 +>> +endobj + +656 0 obj +<< + /Type /Font + /Subtype /Type0 + /BaseFont /GPKBKA+LibertinusSerif-Regular-Identity-H + /Encoding /Identity-H + /DescendantFonts [657 0 R] + /ToUnicode 660 0 R +>> +endobj + +657 0 obj +<< + /Type /Font + /Subtype /CIDFontType0 + /BaseFont /GPKBKA+LibertinusSerif-Regular + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 659 0 R + /DW 0 + /W [0 0 500 1 1 271 2 2 465 3 3 250 4 4 220 5 8 465 9 9 702 10 10 236 11 11 298 12 12 701 13 14 485 15 15 298 16 16 588 17 17 661 18 18 646 19 19 465 20 20 695 21 21 369 22 22 465 23 23 504 24 24 372 25 25 542 26 26 447 27 27 390 28 28 541 29 29 493 30 30 264 31 31 790 32 32 465 33 33 730 34 34 531 35 35 316 36 36 428 37 37 465 38 38 505.99997 39 39 457 40 40 538 41 41 465 42 43 375 44 44 220 45 45 338 46 46 497 47 47 512 48 48 528 49 49 297 50 51 702 52 52 515 53 53 351 54 54 323] +>> +endobj + +658 0 obj +<< + /Length 13 + /Filter /FlateDecode +>> +stream +x +endstream +endobj + +659 0 obj +<< + /Type /FontDescriptor + /FontName /GPKBKA+LibertinusSerif-Regular + /Flags 131078 + /FontBBox [0 -232 1002 736] + /ItalicAngle 0 + /Ascent 894 + /Descent -246 + /CapHeight 658 + /StemV 95.4 + /CIDSet 658 0 R + /FontFile3 661 0 R +>> +endobj + +660 0 obj +<< + /Length 1362 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +54 beginbfchar +<0001> <0069> +<0002> <0031> +<0003> <0020> +<0004> <002E> +<0005> <0034> +<0006> <0032> +<0007> <0033> +<0008> <0035> +<0009> <0051> +<000A> <003A> +<000B> <0028> +<000C> <0044> +<000D> <0046> +<000E> <0053> +<000F> <0029> +<0010> <0042> +<0011> <0055> +<0012> <0043> +<0013> <0036> +<0014> <0041> +<0015> <002A> +<0016> <0037> +<0017> <006F> +<0018> <0072> +<0019> <006E> +<001A> <0065> +<001B> <0073> +<001C> <0050> +<001D> <0062> +<001E> <006C> +<001F> <006D> +<0020> <0038> +<0021> <0048> +<0022> <0075> +<0023> <0074> +<0024> <0063> +<0025> <0039> +<0026> <0064> +<0027> <0061> +<0028> <0068> +<0029> <0030> +<002A> <201C> +<002B> <201D> +<002C> <002C> +<002D> <002D> +<002E> <0076> +<002F> <006B> +<0030> <004C> +<0031> <0049> +<0032> <004F> +<0033> <0051> +<0034> <0079> +<0035> <2022> +<0036> <002F> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +661 0 obj +<< + /Length 5522 + /Filter /FlateDecode + /Subtype /CIDFontType0C +>> +stream +xY Xg]]LVZQv4KK)^QTw ,uv. 嶋rGE[^KRQ+Nw{ǾTЬSy}7<#]x<҈ĈI C#¦ + O +#Z7{ͅBkn_mCwOt? oO?nmM.|U%![#SލMߜ̃3W^ϬXϬ N|f^R..#\x.32mܼ6kmȯz"/#_eU aUQ(QQG;-'>?<'O|^->6vg\< /|rSb:1c`a\Ӹn׎w~S~83Afo\7>xA*^f"؋9` W1j,pKZ)淂(x׵]Y?ak L#ZNNz'7)/{s5{8>]9g6ڸE+ڡ'}!'|G&] ˰II$9-rasxocft-yEB%Uh|1rUZ`?eŞY p&~d 8 +|[QYNeJTiЕ>ǵWȀly +]:e'YS3؅'vC[ 7EPH @DLndA>ko#р -\>WٌF kV91ȋzb=NxSײl0}?O3& g>Tvܴ[$^ǁ/4 55roq.QPQgлh:ڈEfQ|RZ!5N+X?4y>q6)J&T2z~IU(Dk7uJޮ ^XFt\Dْ~K+1թJ&D[Vnixa`PP֘/s=g 2w+uS<OxZ}8㘩ěgO_$ +K042a!ζ^NN+k669Nܞ1Z^|e@ρC.[RBiYYթ")^x +P,d:GdQB FԖpJ/3/B\zֻ߾QE{fِ~Jsm3n+i+'>zyQ5!~/{UGhn}ZU +Ɗ.$g[4l}ш@KWr]i- ;Hyzʇ^,7 $Do"BU~qoI:Zz!BډSY$f[4lSٚni&To"zlרR F˸Ώ>c|xk#|rt[FQP[%Ri~ApF4;b\cFmnu5EΝDEz^JJf$8Bؔb ӶEɻ + u>-̓TJ=(,pu8,r}} [^ʕ{`M'}0ބP5.&P&)BIRh5Uӌ_uyj+NJO:R+ko ~Hw멶֒cGVch؀0*< yR|6ߏ6/\--FKֿKݿ;?ˇ+q2UJ5}@W)Wޕce5(f,cvP6bdXl("+++ilWqʮ@M!R qfW/uu~n?c`)c- +Bc! B_!%S-5u:ka>Fsf +[jjn+s%6*FA'디RԱNgI[4VL:k(f;~uƢY;`tܓ7аi+Ҟ(Zjl?H@F`3.Gב`&ziu=H8K߰I +c&+BAiK&gJEL]#^kg`y,(ܰ^HVHbfVc;w?zJ۹eRm/Oߨkb"l1ʯ`9oQЗ"%z?IՆ$VETB$PJOW O\ (]lcáۆ%e1Wѡ+$Yغz(ͽqUpԩD!u^(^-VXSdJ/CV 9 +W(T: KX SKUl +K-Xѥ"^'[yڷ҅!LzU*i2Y-ͤ%(` x į~z@hjLm˫j,#^@f%)Ҡy)Y8f?t,Crg/} +S`AI!:V~ƎǺ`35FU\3\5l3tL!ٽr8Jj4/V UN5ؙ5d?'5aQ:dsYb-Β)=bTۮg)~5ekQjBZC֑+EXR363GGg`is i.?zT +0yל;Uiؔ$QPxbY[`7:=N`j?=GwVϿ.r k7Jbhk>LaA&)yqnRb|0?vӰut^AĆ>2zke;.Qq7ߣ65ua8Q!ɍ_~ eE +s#DȚt",lGCɤ-TDX#1Rz^I)t&}K81 Kڐ0Y-OF\4TdO5…5([ r$ z?7ߦ̡3J1P~1eJcUdHG*j`Ih-0XASRzLjRRfSY +2C-TL 1ruq|;iMWYr ٬hSD@R9c#:?P:V_]JVr8e'~x_ݴ"n뾧> ϡqLCv'DG~O&$tAOy#lƌ90ʑ[PCYH&tF:&ib[i`@Ệ!t @l?V W&n~-QW:BF#TAEMIt@@܊yzBaՎLx{!xlݭ/!+3K4< k F/}pf\Z2v͍E%4,Y&B* 2vķwΟ5{U-|׎|3c(FϘyH W 5C"Z^LXv1@醚Vd@p!Ps$."1Mrֶ\LRЍaG0Q%OɎĚcE^4hR +B䡠|_;ےAAr태l!7d. +W,/m•M醸or44|:~4pEY66n\D/nf%N!`U`b3B{R`3euq=K5Wؾ7jr3\&ULϛ#-DM.x{tlclf"#!ܧ0Xt#dRVL85q}K)EF,+aa%̴Cؽ0j஀Q~(Yg)N[WLcUXmiz +%"5S%l[R[˔ E P#a ڗ5u ND&  cP%lLBCe+ q<أ0IpFK['L{e,]F4lrkHJqy)odp)>#k['z`τDg]Mɴ=ڷa }Y8΋eS޹SHwhMfFFu^2-?Xch*RS2#;k~^h!OLt'g/?}tCdv/9קU)%~붻`s @۫m5wۏ{adW KD&&:A!u:EZ!OVxBGne}0& eCFNe~vvp,&323&=J$ږM"GgHJv:s7b D; +/9v1K}|Rbv +w<-hq?NlMm +endstream +endobj + +662 0 obj +<< + /Type /Font + /Subtype /Type0 + /BaseFont /JBIFVS+NotoSerifCJKsc-Bold-Identity-H + /Encoding /Identity-H + /DescendantFonts [663 0 R] + /ToUnicode 666 0 R +>> +endobj + +663 0 obj +<< + /Type /Font + /Subtype /CIDFontType0 + /BaseFont /JBIFVS+NotoSerifCJKsc-Bold + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 665 0 R + /DW 0 + /W [0 5 1000] +>> +endobj + +664 0 obj +<< + /Length 9 + /Filter /FlateDecode +>> +stream +x +endstream +endobj + +665 0 obj +<< + /Type /FontDescriptor + /FontName /JBIFVS+NotoSerifCJKsc-Bold + /Flags 131078 + /FontBBox [21 -138 1057 880] + /ItalicAngle 0 + /Ascent 880 + /Descent -120 + /CapHeight 729 + /StemV 168.6 + /CIDSet 664 0 R + /FontFile3 667 0 R +>> +endobj + +666 0 obj +<< + /Length 675 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +5 beginbfchar +<0001> <5B9E> +<0002> <9A8C> +<0003> <540D> +<0004> <79F0> +<0005> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +667 0 obj +<< + /Length 1743 + /Filter /FlateDecode + /Subtype /CIDFontType0C +>> +stream +xm{PT%ܻ7\peנdx֚(@iqe) "e , Ⱥ$nx TE4ѩcUcGG'RݜE;7N;~ߙ;$ $#2 冕?K\ذ%_K%Y- C,*ORa c_ㅗgӧdQ%MtXNE\|}qyaTCɶ‚Mڔ7%$Xm򒷓***u2I3lMK]j ˴:my._UWZ5lԦ [ڥyG{yib}iazCAdSA$YzLCn[9͢|ʈ +f5L^V*{/?n<S*/v 䓖Xf<ADKPI9d4X9-7 ? &)բEaՃaaTr+hiV R'c/,Sɹ@5* * ȈJVM<}jo1dfpF=&%Ιj)hwge ÞnpP`qV4Էq(ee$i`+kb.8p`EKVdeXf`*Zސi;^mȡs本:.Ww/0ҵy5kb-?93hE$2uj)\F{X@!46Z[ tR8GiE15-Sҋ: ױm{%S0 @~WjoeO;6a~udQa)~/qcNǟߋx`sҟ(ν̏S"MY0r2(|C8lfss2!m͑9srwh]S *Svad蝦*c +w0=6a~e|dS9}{`-T*<]LSstuAl41~|b, d,8g/#P_iSy Y[x 뿄ͼ7a|/a<\<[˰9pf}箥@7S`w^wy`ugIWa㎗V +1(Qވ1sT|-1,,64a{< z%Wga:c&q wQ{Cw_r 3kowMtxpڽU<;O-ϵѮ#j6Eh)Eހt +OB@6 i.Dܟ[ݞE`Pk(5:,T&~6 oCP@FtQW^T# `;laXh(8k0IڮJńY-Wl4D)v]YFCFWs\ùV"09ZDI* B F5XF]vPu!OWb6 +;!av|/1^!(`T$#}_28R:EYѮsw`]a~ +b4ȟ>ޝ+FP>58Q̤v}zu$7>~cHa> +endobj + +669 0 obj +<< + /Type /Font + /Subtype /CIDFontType2 + /BaseFont /FLXEBF+LiSu + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 671 0 R + /DW 0 + /CIDToGIDMap /Identity + /W [0 56 1000] +>> +endobj + +670 0 obj +<< + /Length 12 + /Filter /FlateDecode +>> +stream +x #ez +endstream +endobj + +671 0 obj +<< + /Type /FontDescriptor + /FontName /FLXEBF+LiSu + /Flags 131077 + /FontBBox [0 -171.875 1000 664.0625] + /ItalicAngle 0 + /Ascent 859.375 + /Descent -140.625 + /CapHeight 859.375 + /StemV 95.4 + /CIDSet 670 0 R + /FontFile2 673 0 R +>> +endobj + +672 0 obj +<< + /Length 1390 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +56 beginbfchar +<0001> <72B6> +<0002> <6001> +<0003> <8868> +<0004> <793A> +<0005> +<0006> <5F53> +<0007> <524D> +<0008> <4F4D> +<0009> <7F6E> +<000A> <5DF2> +<000B> <8BBF> +<000C> <95EE> +<000D> <7684> +<000E> <89D2> +<000F> <843D> +<0010> <5143> +<0011> <7EC4> +<0012> <68C0> +<0013> <67E5> +<0014> <662F> +<0015> <5426> +<0016> <6240> +<0017> <6709> +<0018> <90FD> +<0019> <751F> +<001A> <6210> +<001B> <540E> +<001C> <7EE7> +<001D> +<001E> <5E76> +<001F> <66F4> +<0020> <65B0> +<0021> <4FE1> +<0022> <606F> +<0023> <76EE> +<0024> <6807> +<0025> <6D4B> +<0026> <8BD5> +<0027> <51FD> +<0028> <6570> +<0029> <5B9E> +<002A> <73B0> +<002B> <4FDD> +<002C> <8BC1> +<002D> <627E> +<002E> <5230> +<002F> <6B65> +<0030> <6700> +<0031> <5C11> +<0032> <8DEF> +<0033> <5F84> +<0034> <5373> +<0035> <8FBE> +<0036> <8FD1> +<0037> <98DF> +<0038> <7269> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +673 0 obj +<< + /Length 10459 + /Filter /FlateDecode +>> +stream +x|WuߙgN.͝;|3F3e潙eY/˲^!11? ڈBBYn#%*DQSQEUVUiUVQU5QVJ>؝|x{9|R^G3t8%$z3Dg>ED#xz,S?/=˟'͵kO=~n? ٵqѷ.3_zkۿLtϓx?~gFt?ԯ~!="zxR zx O]w"7?two7Hȑ@e*PK#㕿F1hD#F?Lk:KM.Җo#C:yINiBj?}my"3}Jվ.Jݜhlf;vSNjav5,}+{1_aٛ.6Ìs`Mv8XlZf)^\x'2޹<ܗ]z9/{)A܃]PfFհj~RrE1&>Nҷ髴ljj99B֫a拽. r!ԈŴ  2=7n.%8=J[MLy64"͇ԜK1G#NEck䢊: }W-̪XO[',j͝s;cUoy7ȉ KNwfvBb3׫p.}< Wyo(/wj<֫f!8 ƢyK۲>\k,8٬e.珙G()9gml[:$7RǓ,4zMc*3䘋cUQyeU*6". ͱ7V"!GDߡ/Qpެ˰ +\{:]~2śVȕRkiⰷLh<ϒ8u!13G͂*l.$chqqFΙ]{XLU,OHa">*xX"9g3b:=G}Tmj;dd !Dl6:1e񁣡y/fC.z5Ʀlق?N#9d13[Uz +VwoȲ뭫*_(":-6to؜ FUc1ZLS& |8 0X-^T=Rk +N":nE[g7O,DT{' DS>„9Kn'n#!HЀ7D]ٿkbk# p7'__dbw#]L}YbH+ ӳcT9t2qu +Y!kp.\Lܹm6؅CC(NT + @g*G)5()@v1O?N1cml#?\{!Mc~s䀖ڠ5MtZ#JF8,'QxND\0m9Rv9LR%{م `Ea.,eK b, K;QQT\gH +vW,^ 8bjM4_F㯈Ĥ|0ɸ fGXVN稾E8941(1(>E2J^ّ8Q)J⇏BJV*XboB3z ) |EWkaOJ71 RIOHYP:(p;m+^YV>;lW5U7ݍAMqEX6O1Oe{|i.C1K*mNQď"kΫ9|%xs`4, GW>bRt +Σ; p&Y,(ZWTz{D_:nz՞3_.8"iONM9}qS'MsyҪIlc9i[^W)癳K;y=fދcxCDu0Q:؛20I.nj}-ndJv])p::cb5v;([̳<*zshdR.OYet#z/XCxŁPF~@ht`9.CH&Q23x("Pvp!5cDY1 *@4Cʫ-+g0EPN> BƥΈ1—DD@s"9dͧ3kRtŲc]ZȾ5 +q3( JLMH]K<쎯ƢqT>N~+"KdK3R~7pulQ,X< _lM#P"nJiQ)̣d\K0ϮDaBJ-OD&~ 7M(:s,>ԉDtΌ >6P49# #0Itއn۵z[K̎>鈰kiu.hӾ:@]U~sNS7\ .(gPBy4wn66SVx,3vTye2:e^f1M:Dv5*Uu8}~>WnܵVljkB\NUL~.l!F/y* I>{=UG < W%hEK3V2KAXF|m2!zuD c]ŕw7ݛCީ(|3\mPef}ZN +{%a7_ %c57Ӻ:9x'MfqF6r`C0Jsk,M|D\ĎrQ6B#-Y2U5@43C0&%lUX-vz7}D3e8 MLk^Z%IUmmkȐSB'sTsĔ4A-NsI UW3 |#'<E8B׭0pFB_G$ 9J.998P08 9V]eNHU;'+C:OuI:|I<+RtQYui)+uzd3j:QM&r0X0Λ@D|3.dd֢-DpIz,ǘsK{)X >?LC>sZNR+UQ9i"M'U#TہaEQyo|IRKKW;*]$,D*{Sv P*.Q/p1n n?=v.ijSttЬ#0}X}B9{g +v 8$E>5mAq0GC+n17:K(yXɇdEANY;'Sͦ:O1+:`2OLcWaՎRU#6%vKH8f 8g.V*O8?Q"s?ܩ|w;|E_%CaAmR{Et{ }{t_z~*eXNuˆvx;% w?\Bz1dnX>SIܸ(x%NʾDQSP{iO!FicNul70jAp4ʜ Y [s(u##?i?Dӏ'356Hhwu]tCWA`Br#f܉*>lA٫y8ps38\y˥880[@ r"JZ'6$֒Pb,4R@M"?EKM |A7n WS4щ3s*. +E{<;zެa(!HҤ+ (8DX`YM^^zj:8`[LNP;KUc;,u&{[ JE YmݓThr2ȱs*qgMxcYJLAQD3f7];K8A$ǰJlB#tǘ@=v@ǐ :J%h  `T;K@ P"?/һGC ԇ:,\3}ڝRڎl^__)j {-C@T?v7eqe^|إrTtNH6,vp@/+I$?zaj;nc0 b/"(g|5 :# )$ڪҢcS(PJcB]YjUe_Da,֩o+6z^OgsED۽pe]"bλ@毹}NS0NśR=&]2\G4cl1;ާSwA|zjn1@*5ܨi$jl hhHŤqC0xMN#<)h/8 xؙr=B,wL.{5IQWSI>\'C//(T4n_Sb +GsAܫT9:I,z*e2VB:@-r=xE>y S?؝G8X24 ïΖj%֌5Rk͗[#9)f|o. bx@ ޳(H v:`[0(5Z[} z _ՠ:e]LކS ^L_[)|8F*X'K>Gq\D̍Bdy4VN<4rY#˨qR2mE?&@/@vA .KE+PxQ nTXz9Ui_ZG}aHn(59w }qr/yG!;%fcy[ov(Kcp?Ww0Qd7/ +#(~̰AsT1Y`? tc6KMuژ +f C-Ƣx%9iEwi߃ &)=8ӽ{OC]}9МS]*J,! lj5 @KTgc*ƦO'.mq#D"" Q(GBМ;1z_2`$zm1;m5uY,xg.ua +bmP;}3,_$@ʔ]|XAs9~&@!KAC(<Wpa79HJ"'}vX2\'n|bZ{VUc`j3Ío~}/IwuSze0hvž +Ը-ѝn8 Ҥi/c/F(@ި*O(4LP?7қ'OQ8^ݜ {:@y%ѽiNr\ j@T ;U,{| "{kPԴAFmpod}Lt9B4܇`:P}=|N9T9M +hXӈN}F0:."v5EXfΦղVUMe 9eHK yo-L፰Ou )h<4SS:Gu xt#Lj}gwp;A"fwCvu\GM2cSv_Mtp3h]Rw0$Zºw޳,*Uv:0eߪ{;݅TwiV锔{;t#Eu*ж:dVC4V}ؖ~4 缁eq^.ʦ^.`PnGk`X:-v+ivzP NCl3wr$Q@"i12B1/FzB0*2+XE]5*-`R}R 4z7w Kw{\v>OO6)8zhxb ΨDlmuba[Y{8nI=0[ʖbt^Cn =t al!Y>&.0u +,5ؗ#̂/ +=@5 ȏfzǷ rZ-J +){C~jMA2ka:T7BY?ܒl?6 +,;L xcW^a,Mߺ StP̠&a$bZ^5au)nu?EMZၐ6|EQT8ݛ/JTrgoG7r*X +E#k< 4$nHzDd6QOFKv`UXt.F_yP>Ѡ/وy4 {zD(vK2wHVG֦tӶ.n}1uKǥ"Š6V|;_禠쏯|Z-P˦@|Z|>FϱCYݫ[BrT{gp+me HH39:Saڪ[7DoYU—t1AI#2_@+w7n|ATdu?87A k?y*5ՙG},C} ^\_A鰞 NgF_77ߓ%GozM{wll=_?^?ozwpSXX~k<o}^o;ߏ5zo*ߌ̛MD{rHCg'C.:.r|5tXoyO I7фAu@gHz?HsC:Iu|60~I=N/)z~>\sBk:#Az^Iz>Zq__DOчY0=G_~wti:ݬ^zV^%-{}t^+{_;z}w|={w=ztc_݆|)> +endobj + +675 0 obj +<< + /Type /Font + /Subtype /CIDFontType0 + /BaseFont /POWWNC+NotoSansCJKsc-Bold + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 677 0 R + /DW 0 + /W [0 23 1000] +>> +endobj + +676 0 obj +<< + /Length 11 + /Filter /FlateDecode +>> +stream +x? +endstream +endobj + +677 0 obj +<< + /Type /FontDescriptor + /FontName /POWWNC+NotoSansCJKsc-Bold + /Flags 131076 + /FontBBox [16 -120 989 880] + /ItalicAngle 0 + /Ascent 880 + /Descent -120 + /CapHeight 733 + /StemV 168.6 + /CIDSet 676 0 R + /FontFile3 679 0 R +>> +endobj + +678 0 obj +<< + /Length 928 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +23 beginbfchar +<0001> <4EBA> +<0002> <5DE5> +<0003> <667A> +<0004> <80FD> +<0005> <5B9E> +<0006> <9A8C> +<0007> <62A5> +<0008> <544A> +<0009> <76EE> +<000A> <5F55> +<000B> <4ECB> +<000C> <7ECD> +<000D> <5185> +<000E> <5BB9> +<000F> <8981> +<0010> <6C42> +<0011> <6B65> +<0012> <9AA4> +<0013> <4E0E> +<0014> <73B0> +<0015> <7ED3> +<0016> <679C> +<0017> <603B> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +679 0 obj +<< + /Length 4631 + /Filter /FlateDecode + /Subtype /CIDFontType0C +>> +stream +xmVgXT֞as60 xtCI4b/(؂ 3Q@@,)3Е( +jĘl$\g\~>׽}z^y|>Y) zHC/]4i,sv̸ѩ"s."5VD9bt4-~2U0"39|>%ޱ+Sk47,z,(:'vIvvS-`i4kʔ)6-{ +\$yxY.߷KDxzɼC<|By<-7SG RzD{"4^~ԻTD)_K k =1g.hcB=O2܋wu_0" :u~/ 0PԦ͸MƜGn7@qc4[Y& ՆҬcC!ݚI![u?;0f(7SM9u(:Y^`0K/s':6nPꆺr|9]0: iMD #|#8E߃Ɯ 0:p_#Ů/=xyeŁ}_3 +VU0=fLmD)fu9jC1Έ229{ +jwpƳ٠S@3:WF 'R¼y%d;g#w/J*%&c=jϟ񿸽<]Ipmf)TZ >ezSf+*{AH!/)'15Z{􋲷=d)ԳʣYT}(&#M71K$Z/w*lަQqɹEBC&Ġ6h`̴rYVJ2#~LżMv'KH )Qfni-iW\INK*QP+;Pr-7[r N()ZoY'<Z<xS;hs+@<+oW)FbQ$LIx8GؾrZڈz"t!PF#89xEm2`yedG;h05mn.;:4Gեr_ށ{ԃo'4SKqN=N}f7PX*>ivߘڐzcA +`+A\iMh.cʬlxmJʑo /ΆsJNgpi|Uo(K^XK; $-/Uj*t vz.;$a'=CO=twVۿ$?azS'حɥb~b8%"?`m/yuѢ0ڐf8!:PhbYhz !q73w[Ͳ-, w/;Qs'k?i" +{~l#8c*;' +q劲`G.F^S@0tM0ͫt{?(@>l!s]rWֵI[b dkxr Rdn܈E /MRҺU8̆l!jpo|R `AB +فR cA8D "J< 3@b0q57Tnܒ]pNV3A7At&8%)*ryJJL)e4ƢܪnS(X./o-WLlqm_W.t߼Enk{=rtSGՏW,q.!h5b$)ӧw'b Ah . 3;(~dw?w2°{{;\ٰ(,8 ]%|9Ë\7>АўY}lK)PG֦j>R0ZȝZ/b'>h3l?}Ɓ&A +t}j|\u3Qs#'TH},Z01 +%hM p FgK 40~n=;1͢SdGx̩-ay` F{$H*C>`8ʲAG%'I6P{Z@pTa?x3` ޸ ߀6TNv[Ne)*7Im F 8޲YG}H}b|#dI~EEKrsZ%:KkuC;=IqBo34<'ىڋQ'<0<0:2<)ϯPrY4@ofn.GK +Wx^`@IM7ܵ'p3Y>8_D0=DiV3Wi6z\4\z>! : z{ &)tTdKAEmsjw$| s.aa2NA5엡="X_F'uΝ +,DG,+hksV,|9I7 5lUBf,z7kV At]՟0,75),~;Kj6.&,mGɘU)j"* +endstream +endobj + +680 0 obj +<< + /Type /Font + /Subtype /Type0 + /BaseFont /TQTFXS+NotoSansCJKsc-Regular-Identity-H + /Encoding /Identity-H + /DescendantFonts [681 0 R] + /ToUnicode 684 0 R +>> +endobj + +681 0 obj +<< + /Type /Font + /Subtype /CIDFontType0 + /BaseFont /TQTFXS+NotoSansCJKsc-Regular + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 683 0 R + /DW 0 + /W [0 23 1000] +>> +endobj + +682 0 obj +<< + /Length 11 + /Filter /FlateDecode +>> +stream +x? +endstream +endobj + +683 0 obj +<< + /Type /FontDescriptor + /FontName /TQTFXS+NotoSansCJKsc-Regular + /Flags 131076 + /FontBBox [28 -120 974 880] + /ItalicAngle 0 + /Ascent 880 + /Descent -120 + /CapHeight 733 + /StemV 95.4 + /CIDSet 682 0 R + /FontFile3 685 0 R +>> +endobj + +684 0 obj +<< + /Length 928 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +23 beginbfchar +<0001> <56FD> +<0002> <9632> +<0003> <79D1> +<0004> <6280> +<0005> <5927> +<0006> <5B66> +<0007> <6559> +<0008> <80B2> +<0009> <8BAD> +<000A> <7EC3> +<000B> <90E8> +<000C> <5236> +<000D> <300A> +<000E> <672C> +<000F> <5B9E> +<0010> <9A8C> +<0011> <62A5> +<0012> <544A> +<0013> <300B> +<0014> <586B> +<0015> <5199> +<0016> <8BF4> +<0017> <660E> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +685 0 obj +<< + /Length 4507 + /Filter /FlateDecode + /Subtype /CIDFontType0C +>> +stream +xmUiPT׶"QNiPDhp@A Q0" v3ӎ `hyEPgA@^&F1&&Ӯ"^[u|{}߷kZk׮%/ +Ӿ +s ݻac`}c 7Зp3P~~ƤL@1mL{}1R?glS,Bkua{|}BbևDG.]b%K-L:`:?**|2l瘲+,dL4 kidkjcjo+1];P7}"= +Bw3&(މIuUu*Jo?|bbVAxvgwcI7.W Ko ŶJB9@2农DgΆa'X.q^zvzU#DWb:a;M dsw&3)S~;tt0-Q՝{?z,s" +oYћ=X ht5;3 =0V0tÍz܏p=p~GIZ֣ͧȵv,aw U gf،4?{Ɏ-~_=4閏 Vk`Nh`auQ?Xh` qE;J6fvE5,a 0"ca/aE9GҨ(l;\iVr_ѫGkզ/*mPx!*EBmTz{%eѷ0}'u\::Fb;q|L37:!y ;K-kQn}w\pۃڂXmڠ~n!+]f~Ud^sU4TȦD#b[_ajNg?Pyhe騈Qӯ+(^҂͝ =|+dYQ&B IHLݞMz&ժ30rQ>Dtgõ TSgK4o\nyq偙1. w߼OWv9ەSZq(捚OoiJXNx*wCu8b + VT N EU]w; nrA!܇E(kɻk|XqÇkҳcrŐW]윒 +,Nn%[QFr#GdJs^J2O [,"> Tl]d0Nj~~S-F2XL-Ht.ݥAɾR4 4qB+);X'-H R1F <[nC ~,Hq+/`K׈1 +/ɕgK5RKP?z-ZԿ;oty*H + VWUn]rw?lՖ|S2:'֫!|xy˾g&xY4R +8_Y| e ?ͦqoo~񕧗Atu8'oDqt, ~ȏcz`:k-sn>b$+>m:y*HrB$aGjI,;4]F4JUɉp [^jqnnlX+>~/6`A,%V|+N "eg5[1a?TꃵZb'zkpmXwr_;g ,ÿ9ɢrP,I~(>%>&9蚣u/ϽxX IVN?w'F X ?[Hǎ VהCH`q[BV;J5hFU^V$l|6c PSCcr'V D XB'gK%#x{q~fYRX[ +1vֱ^4s;OU=Jƾ#C\I/;;/J(۫U7ltޠyj/lB~RS + GETżN8읔H.sP~go͊PF%IKO+(**+ϺR(UcT*?K7;>{A2n44Z)Y 3ϊ0=*Iؖlfȃ6charÁ$"i -h^kaFCwF@ )ENԵL@K6oEZ~*,[s,qRōMyg Jxˋs.#4y,pB8[=֎/Fr[c wxV @{VkU.óho _| ]M6We{Ӳv}e6 ?4J?ڝKD+dS“r*^:Qn3PE"8хyTB=]p.'6؅ Yr.߅ 굏o~u(oC0L3d-CHC5PRc +POyӲ;3!PN 6vj`zFk82p]5  n #?rr1>_/ \5-$IQ#6(3lLdY!iҞƴMe+  +c>Fy[2 쵠UwmQL{eb5^z<[MNqy۾m.)<ůgJN7 9tpRO /KǕfx,YN8Wm*H(Ȋf^G]p`T6~p> V9<㶱` Nj(TiQͽZn6a=,r4.Eg\@~"RK0&hH*IlTt47(]J\I˩7QUu͢ 6ߵ([ ,I +endstream +endobj + +686 0 obj +<< + /Type /Font + /Subtype /Type0 + /BaseFont /BZHWEE+NewCMMath-Book-Identity-H + /Encoding /Identity-H + /DescendantFonts [687 0 R] + /ToUnicode 690 0 R +>> +endobj + +687 0 obj +<< + /Type /Font + /Subtype /CIDFontType0 + /BaseFont /BZHWEE+NewCMMath-Book + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 689 0 R + /DW 0 + /W [0 0 500 1 1 477 2 2 389 3 3 600 4 4 389 5 5 576 6 6 490 7 8 778] +>> +endobj + +688 0 obj +<< + /Length 10 + /Filter /FlateDecode +>> +stream +x +endstream +endobj + +689 0 obj +<< + /Type /FontDescriptor + /FontName /BZHWEE+NewCMMath-Book + /Flags 131076 + /FontBBox [15 -248 722 748] + /ItalicAngle 0 + /Ascent 806 + /Descent -194 + /CapHeight 683 + /StemV 95.4 + /CIDSet 688 0 R + /FontFile3 691 0 R +>> +endobj + +690 0 obj +<< + /Length 729 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +8 beginbfchar +<0001> +<0002> <0028> +<0003> +<0004> <0029> +<0005> <210E> +<0006> +<0007> <003D> +<0008> <002B> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +691 0 obj +<< + /Length 1424 + /Filter /FlateDecode + /Subtype /CIDFontType0C +>> +stream +xm{LSg{-mXk&v ګs1q,0ZZRZJ=hˣbE[PXeCř9 1sqF,\}HT,6-3SLǧT%Au*xB*F#"}:/v!;AИML$^Cgxۘ"9rxo",KUYT$Di;v$Lڹ߯$TJQR9!W'9r5UUr5^!Sj( +(>9T) #=@ D*pYwշS@uJ ph+Q;y0}NoräpN'o# AA3[V-Azf +zbݻ뜬LjTrT6P4!"vjtm;'V#ɹt.B %nz"Y )f*y\E«0L_\H:nq[`] X# DiOc%ggpv]0=2l (MKY!<(T(KDTP_ւ,ـ?'Ϋ4er=(}360=eha|Z2"Vpf''ŷeu pυ{ C˗oݘZ`0YLӇ$It6R\H'wM%E60Q?Hхdw£,j) +%| MGr(*NqМo(1VF8f8\Vk+s =ez vֲMZOfpy`I^^jdCtpf,`0MszSmcO+ȪymDnO-`Z.IHOΞ1hfQx9}( O{u{RIfs3\`}W$sҭv.R( RJ +Ō |o]ÝGp4.ymv:8:K2@/-u/lbf{0I}v5Dk?tܿHmjFjONjfωKG4D>ѹ'OXԵՍ邁\a&)jaFoԻyH]{? nT@-K2Tٔꪰn_H՞o=aw5$[vm Z^sj|sv{ :=XybpT'_!͠YKST[QO9޶vG'FSޭYBgYYd!FHj.2D8Lmk +2Y3 +endstream +endobj + +692 0 obj +<< + /Type /Font + /Subtype /Type0 + /BaseFont /BWCLRD+DejaVuSansMono + /Encoding /Identity-H + /DescendantFonts [693 0 R] + /ToUnicode 696 0 R +>> +endobj + +693 0 obj +<< + /Type /Font + /Subtype /CIDFontType2 + /BaseFont /BWCLRD+DejaVuSansMono + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 695 0 R + /DW 0 + /CIDToGIDMap /Identity + /W [0 71 602.0508] +>> +endobj + +694 0 obj +<< + /Length 11 + /Filter /FlateDecode +>> +stream +x +, +endstream +endobj + +695 0 obj +<< + /Type /FontDescriptor + /FontName /BWCLRD+DejaVuSansMono + /Flags 131077 + /FontBBox [0 -235.83984 602.0508 765.1367] + /ItalicAngle 0 + /Ascent 759.7656 + /Descent -240.23438 + /CapHeight 759.7656 + /StemV 95.4 + /CIDSet 694 0 R + /FontFile2 697 0 R +>> +endobj + +696 0 obj +<< + /Length 1600 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +71 beginbfchar +<0001> <0073> +<0002> <0065> +<0003> <0061> +<0004> <0072> +<0005> <0063> +<0006> <0068> +<0007> <002E> +<0008> <0070> +<0009> <0079> +<000A> <0041> +<000B> <0067> +<000C> <006E> +<000D> <0074> +<000E> <0064> +<000F> <0046> +<0010> <0069> +<0011> <0053> +<0012> <0062> +<0013> <0075> +<0014> <0066> +<0015> <006F> +<0016> <006D> +<0017> <0043> +<0018> <006C> +<0019> <006B> +<001A> <0051> +<001B> <0050> +<001C> <0048> +<001D> <0047> +<001E> <0020> +<001F> <0028> +<0020> <003A> +<0021> <0029> +<0022> <003D> +<0023> <0076> +<0024> <002C> +<0025> <005B> +<0026> <005D> +<0027> <0077> +<0028> <0045> +<0029> <002B> +<002A> <002D> +<002B> <0071> +<002C> <0031> +<002D> <004D> +<002E> <007A> +<002F> <0032> +<0030> <007B> +<0031> <007D> +<0032> <0030> +<0033> <003E> +<0034> <0033> +<0035> <0056> +<0036> <0034> +<0037> <0035> +<0038> <005F> +<0039> <0078> +<003A> <002F> +<003B> <0044> +<003C> <004E> +<003D> <004F> +<003E> <0052> +<003F> <0054> +<0040> <0055> +<0041> <0057> +<0042> <006A> +<0043> <0036> +<0044> <004C> +<0045> <0037> +<0046> <0042> +<0047> <0038> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +697 0 obj +<< + /Length 11797 + /Filter /FlateDecode +>> +stream +x{ xUuﭪ^]ΞN:MbN%MX. d' 3?$0! A8 L@*(a@gЈ<&B[Y@t{NWW:wN5kCˣVIf8 $/W" /[*Mwt=W0wV^:[%@"YP0w\'KAz/*3뱔TSY/H@xV܋p@غhr68d%N¡NkZ,C>a pNG&&Ӱn #~viwV@1>) ?P~N.B=l7&87\ + +@=*5B > g`Ez,\P-C +S ' X=P܂5BOȇ28 v #0Dxx. hAh&bhcMo{"J`ßR&+Uτp6vP!Ud[Vvi6J]qt4MB>] +6g!8 *BtXEPBP @JDJ.!Ap >"<zA>DkPvi5,MvxC)~pn] HaUw%t~{c/%t167';x栄#<Ӡ.WBk|ԳzrfSYܬL +X%)/ Avt@ \s +(5'.R]|psV)YT@t2I tb7,XLQN H`g3;b491b/s )WIAi4EK0&\H=fp7|EedddrYa7)7ق3T[FbZ ΐc#cD=( +9.cb;QZjIB"J3-50f꒩o ߴu 7~){IćǏZݐ>gmͯ^u%w/\RXXf"!<ZP$BNM4H@Wk5-CHFڳÂѠ*j4$|cO<('֓g7ӧo;xy_Oyi?1w/7)bCF ¢!V Br,hdy[uH +!hMDGF_sjʙMjkijM3729*9:ٕS5F1֘ׄԄքYm%J梁-ޣg ;W~bюWСC}^[[?[v_lؒK>~3nd뺽y3sxӫzuܸmd=)xEɄAUq( ۔sͩ)t\dG)j4Dh5Qz*^u,EcmKuii9/:xt=ć~dg+tCJ a沀c + *JW  SZS<{x\A^5I<1gȃ;Gl7S'mCh7(+AVe[94 k9i0 YP-dz"_B xC,T:Pq( QYʹZIhnC2R]j<ët*=l3Բ&OɎWlDްد4CU_7uiW?̓I{sw2Ҳҍ0-'>ȁTk3}r/>;up&0@oXE*H~/S"a'0*T_&zr-9K_u0Ӵ m2H *ׁ<:GHGdQe+>Ni] 9ЧзK(%UTlaUwd8]ῼ4q'*ʙ.xbH^IRAWlx%.e$wS|.mrY3, G}0taI.L=4bos >:ƾ?pյ. +8OCR>zo TdEthRCͻ+aL j(wT[ŸmhY:U>ԹVt!]n5 WnݟWOU' Ib!<>.b߰I6@d}q[mDk.;>F {R"_{ج"Ad(9Ѹ!x"jGQ!FW'<{=kVmZ{/djFsdLd)YN6$MYAYY!YYaYYYYεx-YK׊kZZZSEPEpEHEhEXExEDEdӍiN:%Bg__t9_6,غRX[/d̾YC>^PH􆅾pkJT3ad?4? F1 + hl3uDHE9{_>=BXOugtU]'rA"E K2hH,βe*`|72%X4 QJKss6_ }_Kď4H"K<YBh.=!BP,fPLBh&pWM^4C%9dq'*8]%"447Tws76Kl+Agٔ!B:d**DB?o48^yUbK5 Iӝ_|2N"jlAHWm dHQ$Y9WSfsĩF#]YsTvwmMI$C,9vArTE[3{O+m^orL +f&5P2E:k3*?(%;K0,dj d&0y߰ lq*R, Jr`͕lctLZ8L<0 M rEѐldo~N ]n$5_ "69;9%a]g RNԉouLǕӔ @BgоYhWјn`CoPNs@B9$˽~] +h483, fzxl0b* %J, &T%c3Q0T2I6Y$Hz_O-}y)I/zjrkd (BB 4n 3Ec! 4NrwsГfRl^9Otn4rCiM깧Cx՛>;ߺyd (eN_-M/F^C09(AAvN ndRvHk˴q4ݓƕ,(ƾzޭ%#n{"YE GAPQ0AY($(Br^(%=4'W'Be!`9pg2pObՈyr&r#=Yf7iS,|w;v}o + F`rUuT@P|lTў[($+ blrpIG8uwXXZuq" Qيb@@Q@FgpD*F_c:ky3u_N{BH)}MvQ T,+>Bl[K33d׻$JD՘[z3-;[D;t|oza=0i. :ԡ 8_ F%("4PCth?z:4#IV{V[0_L?C=b_?ćzGg%'Q[߿- ^;GMyZ^m~[w}W1eW'JңQ|px4ǘogt8C!0PihҞ;ttyTcdQlֲܼ7--µ^c=z}N؇(DPr {cOF$Z6k\^GY@+w(t#UR|}p̕(iPoձ{7P?^&uCqX0TQe{#ܕb㥸@Pg1QvCTkl5s+20mW7}2/{رsgٮgP9݄r*G喧>Q[7?{? dmFzGxDMkl  F@$,)>6pā҄4,q8[KoG)fB80l( lLDE8d~6oBijí"C|Sh+/ VܽLh)2JR˔tZ'vBc@r$A-o]SX-C<=6:#6OO߮<9z꛳^|1ѳ]] 5eDZ,e:#B~pwXرFR2Lw %c"7Jp S0 0,eY6JS$"N4CY gC$JhD Iht3I2(OC ۀ>w YobPgE8ԕlgk=ze Ä= g^ 0QH'/4&keV5Mr@iyba#TYE$jNJmP<=U%V9 }]&AegAЈ;wV$eO_#(8z#$$ AlTDIdc:τ`Kl" Roi','$c!(G? +t.N&Mww em㟓QiTsO1Ֆ11'4<ڦ2zjNbP{EOFV6A Doo,aS!8[$QeWl4Dl*DX14)vӜ0d[Q-$ nzFx^Uֺ6퇅ɭמzz&@@& k3(;!|ȁzKs"ܪ+,c YHk{V6 #<$mU'=]`~B?g.ڧo>S[ꓙ٧I蟵>wh F6L&Go=z>6Kz=9@tF97r08EBMX!!nrc5 8d>ryL@S\7Dvz{#) chyOL`DMK56R=<F50˔N}9eJ}^qǻV?hÈ!yRTh1J12J*jObb$1Fy8T]?BŇ\hAv~ /fmu@nd]X}7nte?ouBYպ^x{eG3>K2a)CO?OO g}yITP1Jil#b cF=g&΍\e/?t4:iu k3*FTe}j + + +gMH1ӖG:KδGzJdCoWr7_|l8z֯"žgCSJ|/YX޷_K{Йgh-?! z=!f*V9l/%Q-B.m '!&4 pJ!6A#!6AC$כP +BZXQ>zf OO&әt!Hc?1Ty|^>o  + G >harz4՚ޖ#[֧;MWpӶX`_m0LD@j$}z3`@}/X?F cdw:O:S/"D&D}h]0` 0A,hs !! f*la,0fA!thȁ0!"X02\Xsa)`A0, sfbYh!@1̆E0@4A,tkT8QP`!L_\DH^vw۽-K%\K  W@1$2(bX +s`vRMDX sa9 DM6P _5"(&MY,z| O72kۜ6:[ + ! %+ԨuN@6/ +ڢ!_˙>S%)ҸYib̅9=}Mg?^ϒms cdj9, EZfA\ԡf^/8]eY+K:ǥⱭ[{}M׆g=9e=_pj?rXeN;MԽ\'sXnhwKrIEW1,>uH嚝B]yHniʥ9Y־Yoe0.9iN~ {C`Q4ڣ`,mOc0AP 1;_?JEt\q9a a r4zxt;Ct)s}gq0xM ?58,5tK 10&k+0qw_?O:B;s6oe~A|O; #5ryk; 0n (pum<$45Eϑ+u +ns~?kd%ivum;pZn\S#a, %9i~Vgfه +RB߁ta}Nfe? +? _Žj1n%s=^=?"o}muIi}>tL2?ڲTvƟ;H@C8gfu<1܃*W[>ۆ>ܭFou,p#w-%!-_un͟Pw#NyvgI:bk'us.Ӫ(Ǣ5 s<觻? ׀>*ǩer9tqߘ%i^!I8$j(| $݇(Ϥ{ 0 #0F +a*t0L#Vy Z4 " 00vHi0A(ؠ+@*C/ 0Ob"ǁJk@ l+_+,q#3x#FFA[%0cQ[CcA;f >0|;{~ȭr|k-KoN7%Fm|# /3M*f_W毆f%_ _^ͣ_Vy pǟ3|e!K n㿅⋕ _`/9_́W?A🶘LܴD29?H?f##> ٬3A߯ħ<41|j5~w~;h#'>[ b70m`G?XK#/9o2|W o0\W1Ư ~-ÿ6*ï0o+/3[e}R*g/U^k0+kvҚ< +_.@gxgޑHޑ*ЪJ\aZsۧ֒xixlc ?>{?eZ1ob[x yhy.۬2ެM odxOiKU_ g)5>5cxx͓\W;.'^ +i#'x1̆yRcx EӢJ]<vË^0> +endobj + +699 0 obj +<< + /Type /Font + /Subtype /CIDFontType0 + /BaseFont /PHMRHC+LibertinusSerif-Bold + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 701 0 R + /DW 0 + /W [0 0 500 1 1 730 2 2 514 3 3 256 4 4 250 5 5 315 6 6 734 7 7 545 8 8 504 9 9 315 10 10 514 11 11 654 12 12 514 13 13 732 14 14 706 15 15 514 16 16 740 17 17 433 18 22 514] +>> +endobj + +700 0 obj +<< + /Length 11 + /Filter /FlateDecode +>> +stream +x +endstream +endobj + +701 0 obj +<< + /Type /FontDescriptor + /FontName /PHMRHC+LibertinusSerif-Bold + /Flags 131078 + /FontBBox [0 -229 736 720] + /ItalicAngle 0 + /Ascent 894 + /Descent -246 + /CapHeight 645 + /StemV 168.6 + /CIDSet 700 0 R + /FontFile3 703 0 R +>> +endobj + +702 0 obj +<< + /Length 914 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +22 beginbfchar +<0001> <0051> +<0002> <0031> +<0003> <003A> +<0004> <0020> +<0005> <0028> +<0006> <0044> +<0007> <0046> +<0008> <0053> +<0009> <0029> +<000A> <0032> +<000B> <0042> +<000C> <0033> +<000D> <0055> +<000E> <0043> +<000F> <0034> +<0010> <0041> +<0011> <002A> +<0012> <0035> +<0013> <0036> +<0014> <0037> +<0015> <0038> +<0016> <0039> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +703 0 obj +<< + /Length 2679 + /Filter /FlateDecode + /Subtype /CIDFontType0C +>> +stream +xTyTSg!d2!buj+.q(K%aE*%@6B[Ydqa (!bE޺VrF۩mُt9}w9;wq0ggcwRbSRbL#u$XTFbN)LyrݛAM0{T 㸽2^Mwy9]!M-[/%ŦeJJLIZy . yޓ攤8Ydq02vuyY9H,|{D@:" +nn%0lիzNo`Oc?r9՜Z]j!|>?PH +.\0]Ât^Yjݟh3̼q>G8, ?xEr80yHg90yf;zw-vD$r4>)>0,:BuHS'x|XVQZ.T3vi:SmLV-/9=ZHV?ANNKjW-VvTnt-݂^CNo6CTceWPf傘~.SsY9iYEºsmT)?AW>jZҥ[Tn֥/t + +4bԫ׸Ä9f6lopbb4(F(0nBrnlŷ!PIB%J'Yyhyh);{>xpv_c}]5挽s"-KIE"~΀X9>S:Qp##5m\9h[z+5V9 %v\<3s w=hpcR5L&UXGʴGR 먥 W6:W˔j +,BS,,4LԦR*.ݤ~!ǽܹMSo~[OAXNžI %/s`F|*|]LTB!Z@{*Ax:+rir߷ߴ?@Pj栒(2ȁ.T`ښOD&Sۚ5Ț}CzwW ba~ǡc38.4ʃt$tB<9D$D.L`封KuOXwz".ڦBMEjs Yz֍#.EkV:y1ǺKO Mٹ4jmzbr8fі < lcwh7ϟC2 +rpVL4 î+?sh1@gѨ/P8Fqmצ2#UqMq>^X~Ӄ-Lq^Vilҽk($\S܅W eoaJk %>ap@h u [G>zF9ܻٙr13x&ah4QSIemJ+4j)XIbz}&(.r]B݃)ׅbXc_bsإ &`Z"% +Mz +AvitV:UQxׯCD!<4^>RHĎXU#)>^"dJ+/j j +.*:C*uT-=4..'f_8 N%K23{#izgVK,y0MefJƨ5ZX脡A?m…۰x=f[|_M~&;:>U֐ۇg(p@U랍TL5x4vxc5c&Ԍ64 |@xZB +koXa@8&cvv/q#hMrhU˶ܐ1sxaA37޹|~.Tc:*kk1\d%ˊdMY4~ډ wG +3GxGMd}K N aHך}fL3:z\!-H(r'[jOUq~a͡~[Bh"Uɤ%; E+st)F +endstream +endobj + +704 0 obj +<< + /Type /Font + /Subtype /Type0 + /BaseFont /JYFZMS+KaiTi + /Encoding /Identity-H + /DescendantFonts [705 0 R] + /ToUnicode 708 0 R +>> +endobj + +705 0 obj +<< + /Type /Font + /Subtype /CIDFontType2 + /BaseFont /JYFZMS+KaiTi + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 707 0 R + /DW 0 + /CIDToGIDMap /Identity + /W [0 324 1000] +>> +endobj + +706 0 obj +<< + /Length 13 + /Filter /FlateDecode +>> +stream +x8Y( +endstream +endobj + +707 0 obj +<< + /Type /FontDescriptor + /FontName /JYFZMS+KaiTi + /Flags 131076 + /FontBBox [-11.71875 -144.53125 1015.625 824.21875] + /ItalicAngle 0 + /Ascent 859.375 + /Descent -140.625 + /CapHeight 687.5 + /StemV 95.4 + /CIDSet 706 0 R + /FontFile2 709 0 R +>> +endobj + +708 0 obj +<< + /Length 5220 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +100 beginbfchar +<0001> <4EBA> +<0002> <5DE5> +<0003> <667A> +<0004> <80FD> +<0005> <5B9E> +<0006> <9A8C> +<0007> <62A5> +<0008> <544A> +<0009> <4ECB> +<000A> <7ECD> +<000B> <5185> +<000C> <5BB9> +<000D> <8981> +<000E> <6C42> +<000F> <6B65> +<0010> <9AA4> +<0011> <4E0E> +<0012> <73B0> +<0013> <6DF1> +<0014> <5EA6> +<0015> <4F18> +<0016> <5148> +<0017> <641C> +<0018> <7D22> +<0019> <5E7F> +<001A> <4E00> +<001B> <81F4> +<001C> <4EE3> +<001D> <4EF7> +<001E> <89D2> +<001F> <843D> +<0020> <95EE> +<0021> <9898> +<0022> <542F> +<0023> <53D1> +<0024> <5F0F> +<0025> <98DF> +<0026> <7269> +<0027> <5BFB> +<0028> <627E> +<0029> <6700> +<002A> <8FD1> +<002B> <70B9> +<002C> <8DEF> +<002D> <5F84> +<002E> <7ED3> +<002F> <679C> +<0030> <603B> +<0031> +<0032> <5229> +<0033> <7528> +<0034> <7B97> +<0035> <6CD5> +<0036> <8FF7> +<0037> <5BAB> +<0038> <4E2D> +<0039> <7684> +<003A> <56FA> +<003B> <5B9A> +<003C> <3002> +<003D> +<003E> <5230> +<003F> <8FBE> +<0040> <76EE> +<0041> <6807> +<0042> <6240> +<0043> <9700> +<0044> <884C> +<0045> <52A8> +<0046> <6B21> +<0047> <6570> +<0048> <5C11> +<0049> <4EE5> +<004A> <5904> +<004B> <7406> +<004C> <5177> +<004D> <6709> +<004E> <4E0D> +<004F> <540C> +<0050> <5C0F> +<0051> <8BE5> +<0052> <5408> +<0053> <4E86> +<0054> <9645> +<0055> <548C> +<0056> <8BC4> +<0057> <4F30> +<0058> <5E76> +<0059> <4F7F> +<005A> <66FC> +<005B> <54C8> +<005C> <987F> +<005D> <8DDD> +<005E> <79BB> +<005F> <4F5C> +<0060> <4E3A> +<0061> <51FD> +<0062> <8FDB> +<0063> <6D4B> +<0064> <8BD5> +endbfchar +100 beginbfchar +<0065> <8BBE> +<0066> <8BA1> +<0067> <4E2A> +<0068> <65B0> +<0069> <5403> +<006A> <8C46> +<006B> <77ED> +<006C> <8BBF> +<006D> <5168> +<006E> <90E8> +<006F> <56DB> +<0070> <8FD9> +<0071> <4E49> +<0072> <9002> +<0073> <72B6> +<0074> <6001> +<0075> <7A7A> +<0076> <95F4> +<0077> <4E14> +<0078> <53EF> +<0079> <63A5> +<007A> <53D7> +<007B> <52A0> +<007C> <901F> +<007D> <8FC7> +<007E> <7A0B> +<007F> <5B8C> +<0080> <590D> +<0081> <6742> +<0082> <9AD8> +<0083> <6548> +<0084> <5728> +<0085> <65F6> +<0086> <826F> +<0087> <8D2A> +<0088> <5FC3> +<0089> <7B56> +<008A> <7565> +<008B> <8BA9> +<008C> <662F> +<008D> <524D> +<008E> <5F80> +<008F> <79CD> +<0090> <5FEB> +<0091> <89E3> +<0092> <51B3> +<0093> <4F3C> +<0094> <65B9> +<0095> <901A> +<0096> <6838> +<0097> <90FD> +<0098> <8FD4> +<0099> <56DE> +<009A> +<009B> +<009C> <5217> +<009D> <8868> +<009E> <591F> +<009F> <5F15> +<00A0> <5BFC> +<00A1> <4ECE> +<00A2> <8D77> +<00A3> <636E> +<00A4> <6784> +<00A5> <5FC5> +<00A6> <987B> +<00A7> <9879> +<00A8> <6846> +<00A9> <67B6> +<00AA> <63D0> +<00AB> <4F9B> +<00AC> <786E> +<00AD> <4FDD> +<00AE> <81EA> +<00AF> <5206> +<00B0> <5668> +<00B1> <517C> +<00B2> <6027> +<00B3> <5EFA> +<00B4> <6A21> +<00B5> <9009> +<00B6> <62E9> +<00B7> <5305> +<00B8> <542B> +<00B9> <5197> +<00BA> <4F59> +<00BB> <4FE1> +<00BC> <606F> +<00BD> <793A> +<00BE> <65E0> +<00BF> <8DB3> +<00C0> <9053> +<00C1> <3001> +<00C2> <975E> +<00C3> <8D1F> +<00C4> <5C06> +<00C5> <6839> +<00C6> <5176> +<00C7> <6269> +<00C8> <5C55> +endbfchar +100 beginbfchar +<00C9> <8282> +<00CA> <91CF> +<00CB> <8865> +<00CC> <6B63> +<00CD> <5224> +<00CE> <65AD> +<00CF> <5426> +<00D0> <4EFB> +<00D1> <610F> +<00D2> <4F4D> +<00D3> <7F6E> +<00D4> <601D> +<00D5> <63A2> +<00D6> <6211> +<00D7> <4EEC> +<00D8> <56FE> +<00D9> <7248> +<00DA> <672C> +<00DB> <5373> +<00DC> <8BB0> +<00DD> <5F55> +<00DE> <5DF2> +<00DF> <907F> +<00E0> <514D> +<00E1> <9650> +<00E2> <5FAA> +<00E3> <73AF> +<00E4> <4E0A> +<00E5> <91C7> +<00E6> <6808> +<00E7> <6765> +<00E8> <540E> +<00E9> <51FA> +<00EA> <987A> +<00EB> <5E8F> +<00EC> <7801> +<00ED> <6307> +<00EE> <4EE4> +<00EF> <9010> +<00F0> <5C42> +<00F1> <6837> +<00F2> <961F> +<00F3> <800C> +<00F4> <8BC1> +<00F5> <4F4E> +<00F6> <5B83> +<00F7> <7EA7> +<00F8> <7531> +<00F9> <7D2F> +<00FA> <79EF> +<00FB> <4EC5> +<00FC> <8003> +<00FD> <8651> +<00FE> <4ED8> +<00FF> <8FD8> +<0100> <5165> +<0101> <5BF9> +<0102> <672A> +<0103> <5F97> +<0104> <66F4> +<0105> <5730> +<0106> <671D> +<0107> <5411> +<0108> <8FFD> +<0109> <8E2A> +<010A> <54EA> +<010B> <4E9B> +<010C> <88AB> +<010D> <5750> +<010E> <5E03> +<010F> <5C14> +<0110> <5143> +<0111> <7EC4> +<0112> <5404> +<0113> <60C5> +<0114> <51B5> +<0115> <5173> +<0116> <952E> +<0117> <5F53> +<0118> <67D0> +<0119> <503C> +<011A> <53D6> +<011B> <8FDC> +<011C> <4E24> +<011D> <4E4B> +<011E> <8F83> +<011F> <5927> +<0120> <8005> +<0121> <56E0> +<0122> <8D70> +<0123> <6389> +<0124> <6311> +<0125> <6218> +<0126> <7F51> +<0127> <683C> +<0128> <597D> +<0129> <5269> +<012A> <591A> +<012B> <9020> +<012C> <5F3A> +endbfchar +24 beginbfchar +<012D> <53CA> +<012E> <4E09> +<012F> <4ECD> +<0130> <7136> +<0131> <771F> +<0132> <4E0B> +<0133> <754C> +<0134> <5C40> +<0135> <4F46> +<0136> <5E38> +<0137> <91CD> +<0138> <79FB> +<0139> <76F4> +<013A> <52A1> +<013B> <4F8B> +<013C> <6761> +<013D> <957F> +<013E> <5747> +<013F> <7EA6> +<0140> <4E8E> +<0141> <6EE1> +<0142> <51CF> +<0143> <6210> +<0144> <529F> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +709 0 obj +<< + /Length 54376 + /Filter /FlateDecode +>> +stream +xyYU'ǵ᛿ýssHLM!M&I +(q(RK%V0h[0ڶʈ.[mAD ,.QQK;Fùgf# NDѕW÷! B"gM@~B}ox;B~KS~#6kg_"/#^<!OO/GN;G}SozmO?yo}٧E%,}gG_~BO A=3H",(F-?~F/MЇB#4BK >tE=gZ|dдNt=FoF34CT! |=g8"B4:,k"Pq"}Da`~m꺆jl6U]>?8,g{{#+j pV Zky02ȡϢizM٤ɵ`^}|_^sc=,bQѲ?8S!hd<1'*@P8Cè@wע'kXݸq}UVUsuf@ ZmVgk;7U:U=cl:ace8`W Zp@FA*3cY(O` Bs"\ S_涪8SEɸm듂 +VU%EyL)/C,@eх\ [*g m}4aL2ˬ|WƓmgDCF-NН!6zumN!Vij +vגA^pt]]~Vej}Ap"f}{VFe[={JdE|&!pf]X3:ֲC_WTAkhb_]E[ѷ܌(Է٬W[#X%:iv'M\boLikr!nj!`9k~c(f 1EgbgHmY4-sZj`A~H gt*r3F A) +W6 3̸ .9=c3At:xc̙ØV[&`.Ťe:U&fr" ]{P8a]gF~ݏވ&dOi{[.6AfY-˛>\yƽRkOo$ޜ^rVJ&z +W4Vc3 P uk7T0Xb0C`dVIZ;D'jue#z[a xgk)}(jz|5`T.dsT fA^u>+-,fZ/ 0(gD_@FG螭q)fsrmp{HqmM&asot1xT:O}pa +<㙖Jڻ,vɬBfʬN)`|Bp:d€^BH0&`Ιf)"cQ~c(GtyߥxQ%9dJm}؜f5xrA`̢l*^;׎79T n sy4/e,<b_ 3QsEQ@WA_|Ϸq@ns<%>0zQꓓtsr!DݬN"Gװ"Le` +MlFqYB2YHe=3g.z:pycB)kFaq7(PU.I1<F>y70l'n`QhH[lB. עW$$rj^HaS/bV JX׫_;4E&yT +W va?]\0TXuF9\L8cƉ,v ˱NQD ^dTNr=0q+~OXq  aT9lgaSR ʱg S +J?`1㌚AAz~$PRJĹV020H#B+ʁhN30+)%'r*$ZlQI !BFKtb[t][%iǪdifjRKZmw60טF 4M38Ⱦ8`6":Z˗lÝUS ڵ9P+%܄rjPqYX{%,QT}!7 += >TVkz}rjt=d~?^q/U S 'dK!pVw}2ωo枽G([L*(C)Ce,᭑Մ U=ǫȧKOEēi&ٟ/_F#\ϢG]UEBj6V7N 54 l?OS*Yi4o[,1Q£MB M*mWf>7y2lV"2LzUWlNjӼCx)VJRyLK˼㉜f PΤSp}JrGF((u~Q[p^qc%KLe!gFR0)h%EvF"ϰ|ޡ*f2LSf@/J]YJbl/  9Ɨ́[$#PI)dQR5Per8!a'(;2c*\)thHGsG]z= =jA-hˮd#?|ͯ[m|0X /_s+^ȔG5c@g{۬V=X]؋H:y,Lj"{EKhDO%M6[ͮޒD'Nwiqw͒x/P_j ;y8Sʭ4NǦfu9 .~$+fB=ɉ]o[3ZL$q0jS!e8&1903 BAV%+Bxydk; @'@@kt/z=߳{jGwszoPs{MMX)0`-VqQ謲BJ̖97*ᢵ)?CghnWmyw[n0ۑ >|W/O9Ms~xoo^Ua?ˆK)BnSF=Y5k lWƬnV&"Ahs8ƅڱ cnNHxZ릻x"J)e:(0UXEP,{HM˩εF /?GG GoFbeҾv7릿nk[*VuU[898eAP*T4M({5#$Ӻõ*'x?>X *%pu5 yAYay@lJ;"H_j 5i&>:{n>Â](j.*f8XR\+[;P?Ȋf:̉ԚF"ĕOs<g5G`1$؅ w)?E_BADiZtKȹ}y;2ĶܜJc"'Qmj%@w BVSx +Jg7JL +:vu6Bl̄59LJe87T2%U$8K0~6Jw1k08 ӀQ( +GM5f%L圧I ֈ(s SB1X!+5%+@ӹ&]rAEʂ'Dь g0>C@O|iK*{ntG'TSofW&7Nw'= q·ni3#0=__`Yg<[q FIN܁55@ 8E~po@HRβ2!!'FaYDpM陲C\q.t%f)N6iӃXbi8 +!Y;o zp[/~)tjri R%_s;t!P&g_tXAފ\}@XOhuwk4"(}-ŞYŌ[Λֻ8щ-l6V:SmNW۬,}p~r/vIZǂ1"}*8%*<`y\Vq9ZErOlvf4*(*x?Q*LS?oKyגLn0JwC +4e1הiǘҌ!XS`PeQm-Q>>~ tNtRPm+ۓNӻN]V{*QqA̅CWֶJi }K if7o8,EЃÜ8=V*7Y85XAy/j7\܀ZCSGPdoof^x~rj U(B+:SokmXtYp9&Bv|:jgemiZlzy晔TҪelG$O ]t}2Z-)6msܝ|S}[_߂X}YGSѢ3NR(UleLMե9#K\I+p4 +%mY=Rл.5ZF$ЎPY$ӆMXK%e5gbxtO ,9lXAJ٧0F +z)zz}+s⦛F{/}yfH^\m.9dz:=ylRV7Ⱦ(ʚjPMan 6'Vs|Trn.炼0fB[G@ _% Xz$dŲ\PZix,BAX)⻁kP(ț*,-8g9~/M¨'h8 +Safrc B 58c]bšt`: +B^KgJ~Я;}=6M4}0[.ZSYi=ד1̤a5S |ʚ2' BpFe|vpu(Gj)H%^k#꒡+Vg#5+96{&z=, WYnUvK vcI*feu0oٹ>!9nZIy]{{5)eU LC=>-x5RAbJ/YkTL^$LeK3K\HAmS?RD8Uק\ G~gsfBua/(Av'R$*@RӉWB~%K +*?bA]|8xc\H_ЇP*=0wR $u.?x?Z\D 6m,*S^*ˣ~ T,;:L.L +ZE {G3]󠇃H9?ȇR2^+9ѥ2 WgG~==v*m[oզ~)R m~ӵ;b:vZ>͆RFƹM/M} Qql4-+5 fUc)΀& {A9h0p2E{BuV$<>: xsÍ:툣nzlӎ*ؒ]&vy0e0Yr#FS\ 4 |Ǵ_ͼK<ɻLiZtzArWGgHyM2u*?c~5hs'dk6.]k9J?~Ki&fl50l^iմʕ]qZKwzF\4EK&RXST0QK8J`+kmYePᚔ",wKY^;sAtz9>I7)}tW'˭\1]H.!lK29$Mr?hnLxVީj5^ PF΢ӊAx& kL| A6MdHfta]kkM$ÌL1FN($ EFdM5Xk' Bj貃5@C>a8ێL{GxT Ϡ%J!%d!EvEo5'}{[/t_I-$x(ʻb-fBBWe57>:{E^uD&^EF =Ʃ&P.!)W3`AReaE3_@_^μ{YP0SIM{vIۅl~6U4? +7V5GGiGSKȢ,7;@=`)Hx@_Q8:4cRRZ52/2SZH8a`T(v11 ' +s6yLNJw̨˶QJf5q11ŚRzkƀBI"@K! U+* +o& Z*.E!"k}}+z%aohǥ[m'VD9?oNxp}**ΆMXg&.O*_j3!z=9א+0 $\LcgG+Л{-kf7ֻ{]X[ynVlSXDEչőJK \ij<X;'K`4Ν22g2ZP &e%bPz_Ę1$BIJW37̩4sq5pc)UJ|7aP1c`V &qgŒm>r4@G=^yӛm}ɵf5/}8[oc2;UWd9䰼[WƢY^#:`yT{ՔTc0St0p̔ 2o;TO)wSX +! gCoFfSVP?VEcJ}]jx1t=׎jSܲvBc M&[j4):^;5sݠ]&}#rW@܃'LA<(e* +{aì`"N +OVt [4Z%Dʘ$onN͉d,fb/n{OT9Khcve`wE'Ʌݒ[Qܪnvbga5>LJ +-:OaA9PȍæR@.sk|yJT؋hadGaaSJ(=ZY Fk N +켸V_J*,&DNqhrS(Cst桄Tn+%4G*JJֹލmΩSsuuQO}VDڋ Fl^>lyqiϱ$ŃǦvI/ P$0 X8$q`D@4eOGe`>rh,f t! e5Cte1;iw-‡*7@'ћgCL9S锖+sP7ٴW#)9|ju1FskI騌-aWA ugE>^(zїөl_}՛:Nܚ`c ! XSrT!Bb>3Q? M fCQK)''ErVH_i3yWrbK!M->:zrr yw5 Q/@XgO~Uh*i7gL KjIYnMĀHZ7=+0e:ώ+0k[a.FBHHe}, sFrq\4R)8 {ڱ[%"Buケ$?2s%TR95 +pt}ݍ⁞9Gѷ7;PQ[rr,'ekdjC7ujB(z4qmyW9duu޾\x"_DfG8b ӂ;q/1 + +[4%FZ 1?k$;).bu+('!ֹU&TJ FgEC dj\7S%)9*͓n]CG5g@݅E_SR-k'(=huK8TxJ1m.QۯwQeb%Ki/lZ{Ky UM0wJ ]%%ՒUa(AGFkQ уXN9ɝY1\IV;>h}H9ЇmvlԸI3`'5ުλH7Lqxr5+!睓N[a5]L䮞W聂W6,Z'ξޏUE}1c ꫃&Y5j9\U3u\[دi4ݛ>^'BX_++`V=q֭kMhq4,C;U;%qmN_v?}ykIHi`q(k`b>*Q'ӨlrC1M}xq̲)QӌrqLf]_HTr!*+Cż8Kկ-xuas9õ~)i[ keѧ ~RM護eRr'-2@ݧEvcRkMҝndM +dzSGכ[~ʔT2Up3LPl5f@ҭT>aCㅹ.njC`\;h.IƤȚySŁ5Fb0Cʘ5 Uubx?10Z}HP,"!-K^PX$Θ:z I I1c +g>~utobS^{[Se4b:H/(Nat1a16T=Xi-tc-,[P9P5(׋ٴ |Xciyu0,c}W#ƛp;}<6lysɮe̍&d `o8Fdne_ctD6wK]p]7̏ApZHʖy9, -Iek3K#EmEf G@),)PRV g_BG@:DW7gnWW~~toudT h^jk9l1pUqY%}Mm[wIRqm5(nIec5Kʲ4ty{wy0fϘ XbmewJvՓ PM*Wy]EN%r.0̩8c'PVՌϴ̫,B1Eq{tܛXO/B]xwT?{;=ٮH 3Eoj .*i,k J,EV'ubRS1`@=J416 VN|91*u%M5ti"}}}YԡvV6jV79My^c˗CI̴d?i?&)+40v3Ko)0fc'/3}Sܫv1)IqkSAwe?U%o($ 00eXeQWs#i.ߣq@pVs7m}k0o+TnoG_%dJo6] E\,PzCBokxFd~6J.KԱ"VxoDT`_H_&yv9`ҾTm$>sK%z +ӛ7*l|'HI|-E鹨o}}]0 QcS?U]yvIiʆ)iӧeuS%=lS+ #ZKE8`afiĄpV`ι@K4L;"R6ME(H6I<& x1pIAL"لcihvS?٧_]CwzЩ%Joαz'luz}Z 4]7o ze Q A3|ivgUI\ti.vPAҀ1'W^, %:Jo ~e|bWd?CEzmښ7i@֡RI]ookas))ӃQvTjvle^JX)oX_-4w l4eUO" 31LL4Mqýqa&Oa̭[.;94Az;s9nITyol+]mN#Xg^Z܍sS_т)n8ʜ'MX/˯ՙsfN${xj9,_D0FOD(!%i(GUzRoU7nޮC@MČy,MkIEvѢ]oRp=%ևMU蟢Ϣ%=j˅^rvEV[wc~}b]{S^N$Vr8m5/}1W v{i2c MFڍX1#T8mb[ͷufQK6bf|0K>2]FU)H3)pVhttq4><4D]Ȯ۪d[jj_okU;!ȗ} OM3 T98!qV O(BS,Y{#wuꮸUug"E{ pP*ˣ{E@abz%uS FtӡjyiE{v_7rzspN9 +ڱ)qT>Z +u|<‚ j]9LW}tvj*Cs:*b y+QMšE , 1U3V1&OؔBq{sfљOžzm{9chvTDIĹ v#}HfգN:,ؤȵD5xo9.)(Ǽؑ?1oqp]1cf]L{8w"&HnS+4/K]4RTGkj;"JE7VInk-mdogov_:W(-eQDiɄ"%US< +NO `ʶd<:'+J`DI{.CF+bx1cg Ӄ8.kN-h)ų:9+ܮI#:7N|Y/v:zwopټ%z2VMMQ6y_M[JȎ +JjhCt`4aK p &T *TeAKz -(&(b0miR!L83g>>2t=k^n5ݸE>nO=O)HA:9"6~KctgZl]pm 2ZG[\3WpTJjJ/%cjz k;8'W*:G4D,3MOW"؂SbsiHuͣH +S ͽ^/ + 5A#ٸf1wzTjuW$%ec |:F:T#/q$ ?PU㰚dTiL(eߌ3G1+7P'EHAg> >nHރ`pL7,ݻ4{G?6λ$i7lI_/L(P EaW20rCҐ8LA(9F (ۣRo&,\ml?8$N+]Jw%riƶbj3*VpNfY]06"*7"ng~4[:oE1eӽmAÎ/:=Cy;uNSny8X"Ab)"5QgaGuj L64?r.0?xM' nWo]~yJj +HxNa6ʯeT6/2&S&|9l[Yap"kR+\`bI%-w(RvA35&o$NKr.PxOT h2 Wբd0qꤔIY\a4Xᘪj6i",# + $#ntUx 6 ㇛`9噯+N׶'Q|b,R}W}_7 +.J.ᵫ裓h.ƣWvDPL )mɕܪRѕ(,WxȫHtvې, +Mx{ޱqwQq[k8 XbdX$pb}pDxaa)GNLff΋AŃ+*rY2IyL MDМ ި=&#crm>3ɆU05a~ZqdH KGswY([ U`&bVؘ %}B6x xx~Gez}nlYcOM)Iv'yH'NJrAADl1e⢱[`ՇWo]˧۫yi@j+=Um# Cvr;A3Lq'$N}x $S T sq485kS(UPpܕ;u ) dR&Y) ѝC\(44@v~!o@ׇW >0 |4ِZ-D7NH#iH*ke(Ӧ*eTLDX) )Je\(Z?O +?$8r9E|MeArK?y>u+J t9t|| TJ?NH8 +cpݧY_E+jʢ/1,\^t:1H#^oOCDӂxRV3pҽx'Uf䲠g۠{_n>`rƝmNTq 9&jYmvLsyoܲgbij >yn{+i[j$v:W$NR=߿wS ZYuoXw;cرM0~Ķ)#Paj4aaD5f4W]Q&8<~Z)$x FZ@fT!CFK|,`i~}uG +еŦK, ߼I,0A*1vi:#u75%2)M!Jw)M4LAinOHњ6i*ξ>~h$\ȅ}hW\$[ey6gC 4pK_[9L>l=)Tfz9ɏ&B-%>G=L"9z)TZ9VX("e=_]cw}{9L^{_DeYR/6\L(yo}`R9|~ŜA)4I"T&)*GM曜r}Iϊ3!L;egHNs5LeJL++`<^8Ew ۿyn틗1Let即Wv>,='.rIei{/Yn.2xuxrM(pYۙ"mӒ2定e—Lv'"(8I'-"y+vWRY&-NoU,JWAܙ5bSƶ`3drpNbB6ۻD7l[G1WP' +IJRۦ㢮EO2j5g8/锩SJ LsM WժCˀM㲠u]nSӌdŰu@yW /q%wI8Aӧ?m~n,Baѵ-6:iaVLP Y\8T>6RcGś/)0_ wph_G;°Rzq.@ۗ wIQןw4e7/VVm <.fFZ3mN@}Ql ͨYG&Mͯ($FjPeMracLbs{ g#D1#4"dєDwXA.¼JʩIm ^Pb߃vm?uMv[zK;V;߮Lo?^lc SMڏ;zdN$ZBRmvϓsEov(sb ES 3T'!_:K9*k<(;pVTeWg>~,{GKj^tRi#y2 6AYr/Qr<۾38j7,SۇceFYBwZڪZ\eZ1YƗTWS疰UV6>(n2=l'11qeW9x_T ξ>> +Fc_^F7Ȕe@Mx}RʏStXN9ҕ۱{YlpvJJrn9#ғU;e0N9BCS8#o00m\sCsQ45ږ|d o.ni].m'}^ +KC}C۫q*DBz~"T`S$S6[\:ԔK ,:ONKu8d,zJ1nfTǞ9(나%D 2ݭW[JBהҟ~Z_ w]7_qôgh4ʴn~^Գjk(mSCsAr=%$D豗 /nrop6t? > I*a Ihh z}=)zgjr_0f{[?c{ڠ׹}+Z`oSnC #r|,%O:9]v#JFb֎9&HWH;hSڷiiBիtS +v!ʭDı/ f/;,qDJ.bZ4X+#cȓ'ur"L(:om$#($5fri۹|| +X)ko*=hO8ꃝZ#Ob1+RaZzh\:v# EZ7WK4τ"o3/'Iuj5xAXɦyi'NNdLq,ۈ],|dBR~:-/%>|QK_ KÖg|awҪ)Ex((ܖd:1ywt=w? +#Oo@bT#W2"xB쐱5ar;SOT@h^C~PrS;Um6QRpu$ʠjec9G,"CdχWO~`2U4cJ Yhj7b^R$bF%wb[1:4%DB$BC*3?EQHˈU׀ Ue8r["IM%]qg&! ڝB>9pȂݬf't5uᭅ@_CH*GC[Zs(R)_WY)̓jx SyRkѭU(E[y[!M)햤 bN-T*@}%Qp yERW Brn׮u9K/6Ӕ;/rk5Ƅ >~^ik~ ١>}=5>3\shU>ı`}ֵ-z]2O;) ~{wʫq${ #6' 2-HҐIj!?L՛;s +DWtMFcNqR{Hp] r j?mr2CMLV&)lY9h q3TL.pUoaZo4B}żӣ2!Ʒ+籌7fZDRL)'viEv/sC$0Y1~wt6䶽yTwZ`1OG>F^Y#_psDcn]z*iFׂp7sC]tqNaJuZs/'D(9tVNOE!e[$W`/O .zw\:maKֻ+E&S?LZU DRluLbh873cx78$4AZc쪆0[!YNYszι!J!72DoL6f` +9'^|^&/]jIa@ C{;zCsϟ;Mgd3ocpXg-HIv|W~$N0XӾ7׃w+޹=ω+; YlΥ$,^acNID,ɺʹ;iXB2\3 ((Ʃ CM0 yngQX8if?4&T&qW`A,:?3[f^Ii Z@"HȢ$QbdE{YDLar&89}r-oy|:aRmu?.]qJOS.:cgT-u2m7%,Rzq=J/B) +Z-0T{VK&wˆ6:> Ly +(8{{.%dž.1TXejP~%6ma%Eymm)5W6Tyl|  }uFq5B&1jE$E1D{"De90wC*i/ Ӡ.٦m#`X[QeP%/>1AfTZfAJ.#jw/L8b'-Zw_C*I+G1n +z6#}kXLgù ~wm5Napz};#t)aA>-K<2G38NEW8S_CbZU-h9Nk´s/#͡'axJAZ57 ;:7ϟD/4Ye$\z{u{~WZ]o{ +2# ("b9)fF(#ڋ"y>0"9)^ZormgQj\TP"s9r:3sG(q$͸tMDNh>ѥTy~)b򑃀$rh"BEp譊Y]`|j{ɾ3wCO4 \4H*Os,xNCzΓLڷ}Xd,- Pf@,*N`^< XxϾ>mLDA' +,o/'dl$Er#c3ijMZ$ B7l3pF(2-_"QlVQ5<+mI{Z5BxH~%kFm^;}ˑ=fZ&$6,qj4/l1miB"0cPr,@_ +7WH ?(LĤZS;PBr-`aX;o.ꗾdJ@ބk|9\tJVD=R(p?<3ݽKat_ܔRms{_۔QƗ^- %HQb%kɭT8`U0D9or9)\g)&l4 2NLOCfV”"~MD1{:}-Tݞ:ʮ +"Avq4ĭDPֶ:DELnqr&l%}_fX#(L**xTg_jIAy"/{wyzn#-`WZh*܌3p1b +"%RaQ'bxAH#D߶pp/ KqXY!ntruu!5y܌D35 +%j<3BG"uS_O_ +e5c}i ^2)NQ3,Uv,F!Vs#KPOsϠqvGLQ8I¤BEUz>(5ny QtVkHNJ`Η0޷T}Upc0+VCh}%0l.,7I|GsƵ3!ͨp6u۷mɭFk7RɞJy˕r9ǣRc 2`ĞX^w %xH #OO{@ΖKa'iԃfXρ_BQ5D 1*EAR+`4 +TAJ9RyUdgӣ#)o ԥTT~Co14#]=4j!_G+#!L)<\(Ad[TWNju#&9=ZN!AD4uP? >^hLk?BfȨ6>WA,M{B$ZS:J-T-Y8z"*]tL` Ht +H2cT^' 'EgA$;Q9 c{ <+@B>bhu_d_Kma'1;h{Q)zct`NK%V#xg(P^'󊔙A3 +"Ջe"]L{1 +k%߸꫇& eEei>PAf'RB?ۧ$AEd KwY9 ώWǷ3̹$Ԭ3w%cǹ5E}絇(Bⲩ"ggS?{ )BޝDT?>fn/o~ݲt)>A>{6Δ<,E#^:}KzHaLz;"['o&ɼ]ȭkM=f:/}5Vge;eRQN}Vqbq}ĶСSmDC{NF,ڢʫ* g +>>C(];8!:TCnwR}:O.}XحR\>S`$V^GBltUOZu1_V8U1[uX*ei[VJ3b {>LRT8cpA(8Hwc/Ma\, bg;9g.!RѢ4")\ űa&J4+@bA_ז}#Zc bCx? ~PG K;kz߆WNpyEf(3b2jE#ƙ7#$F^&4@.&E<,-+3s ]A4)>~fw 뀥׆y{RNa~ i%Rߔ؝"ޅEzPj9dQlc"xB)u@$Vx) ?xAF"[/JE"-U +]@b)1e`L'ߤeRT7NC\ ?K|[ʣゝZY 5,:eӀQqpֺZoɕM.K> ). )Ol`ʓ\:wDI=֘Yq1J"dcBUR*8AE>gq4V.eYtB@SRX#DE$̅$Cҷ͌9$hzΕDAl (# M_^RJqe(3$#Qb|lq"MF`!blt)SyKճ  6hg^(6w؃=Eq ٨)lj蔇eG)|r +S/4Es念nIUC`RHQvts<^t /# χCϴ +_w۱=` D?(;ΥN161]!:**U-"Qq}M "n܌4o,oSrt0#a`5#/:Ύ> ;yP1/F%u1wҊ_qYe+U|s?;NbڒvS6TC|fa/h?q0,%$dCD(2DG'u:-Kjr(1ШwR"}}xr6dyQPN!I9EU91d5P$+F8qLfq$ӋYh1 ϲRv>ou?.9> 2ǹ_Ksajȟߨl@EJ@GDemʜ^80`UkXҦCpNk,s'l'Mv |v%,8 p^`DTnƧJOH$<2X`hjK6+D!L_^ucIö<8 O4LL/q9V튃ޚ/^^_2;(pB{=)Ey4i*S:A3=ٔwv4; +"W1z,M6>f\kwNJA4r!4}j-IY@g2~BQw[{^-w({6{pB|3Ljmm;:(m&a ZX=;63\sHX`o1̛r'F y>xƺBp LtQ+V ̰Zz'To:5M얗Hp +zT)ƌbiUZ̥3X7 +ٟsc8x xUO:Ǐ^kMҪ30/8y`gQ(^>7ڬ1LDz$X-|H \]5i]#l>CZt4`C9FC)94'1Ҧ[ϏJn$[1A7.J!(\1 }g}||  cirܵ!\^c}R;V$b/¿MF_/?4-j-L̄bs\eCq b a׈a3F[z~ٹDkES`L +Bh10\4+t9.57&̫KKEqԣWm0$E1be2V!S I}c+7"B28&Ci]"Q-JJG1y% NId)}{kA^~N P'*se{U+r|(߫U95b51RB6[fdY\ŔcΈ0:u 9Vin:VN9C5Qa^⮟XE>>Ԍ2*e{kWj52uy{aJ?LIygTOŠك~V< 1Snk|,ިJ&g&3-ԭd\B̚[6XUy00B(܇ +8BBYѨ+5fTSo򝛫?ReuߥSqRsWKp6m h=b '>Qs }E^ɾ~|ؓivwIPd/lݡ칮='?`?]9Zu6n'{V>S o(~j̓e5y˟[ĉt׆9D&hϟQDdLG..h*mƄNok %B&#^Ql[bXjbkb i>- >-S ssRٿ*ftDxq0]’pGAe@߂/ooԧ}~vӝ/|{/Xkw$I"˲=;MAx=P$Jiʪ8Z Ջզ3H)b23]֭JEe0 ؇H䣁aH4d5tXJ%1k.]!Yvp˪8ΛF\X4$Yr(0%LXB2`iN G(40pHGG[s.]I)T/v} Ӯ6ܶPnǕ [8$'8CBe(6o +(Fa ?~@HHc >Qn07[u߹S=^28୭|.s7܎9m ++/S``y&Y!2X㪨}hGk]P=KW![DCKĤG`O&SdV,H‰-J54Χ\f%6:يQy_"B0 rDFh\_[x9]QiYpK6!Ĭ[yd 4VWw`cBXoYm)•ĘӃNBo$R3%Ziz]Rg%3> j!$*e,l!QCl +\5?"#X[iTϚ"I&|46QF,.`^\}"D!uc'LYD"$$HJK\F`_>^{dA$,m &k]Bx~ LCIndN:UGhI0Bh%\AgfT(%1ϸ<4FF}d+ +2ɩI}E5Hs/P? M0MaP1IPeal=ٻu~,SR߲y-M,Vt,>hˎo#Ȩe+^0>;*U-uqT'W2ZhN١쬏 ll,r4Sg.Pt?ӪbDG.8d7Jy9;QaH;hIN|NuP~k 1l*]7ZͫYj~$V'i>vIȌ@Yg_nқ>W?g\n[v9$OJG؛ٶ}qkyw>9wWJh$ftF; J`LP$qL + 9·T1 1u]k>W8[]J{ZTٞD CjlH4;'gJ0ERZBbW16 +.VUAd:l@uCz==5}vAK +`T[/В<I9@krܔ1wA +).մUuuC^3'UqTC( d(܊ǘdvD.rrr- +16ԖdlHGgw'fSYfϮvleGݫw̋6R|%K,~a`,o#Һv2+@Xݵ&JUהUJ `(L-yUMlmw^0-21L?5m)3&~NJ)s2:$},z͗2aֱcy!9FɈ^F',L+ +ɄpL#:/+ ijXoqsyn&X"/SKj`B1T7$zhn&)F|^UP^ 5pyƀKcBеtи~4Dz6[fGj'Ox)6zQ,X#YڜZa[Lf6L_mkH%}yQSazEKJxiwC㼜 r pGϦI*+fHoc{tjvݖiaƄjB #0,@XԋyIᰒN5`?Ȳ_gv诼Lo~Bv?}ݢTz50-x֤6c]!}TNT/^{zuLVHQZ#>ʺ 4\W:X= /F3YL1c1#_5: @9 N**PcĜR} <28"XEMpre2eԭB.^GB_`v0lӞ7"\Kv]T2\UiC" PEWB LL.&a{p- l0DwYdZKidEgr?#Ql=%c]m278MZ+%[MP=ňK)ae)Dp#mVsEҗq'({ {/ RX_4B- d/88;a[C_Ty-Of)O9*}( +=_S8y B+;pťwXI:Or [&h)E*f\.ra]5g}"d3zʮ!8eVDvI6[>~_X4V`$viZ` W@גV!Yj2S Z4Sd_D"i<T +&^OuiFc(Gl/\b7x 7ܓL]aK塇EJax}T&4m])k?Ίf.n'=< LՕmC"l@݋[)a@L9/_Ⱦ0{[>hvR6v?=4ePئNL\Tw5@\f!KyRJjFAm!:Y}kCU.m<;Ǣtq>f߶ ϘDf@͝4y)PH۹d/Z7XX\[Zq<" Js#xDK R5i *B:Jgfg/>'{gˍse|z]PZ X*vaS1-6Ԫ74Z%]GpN(ͭke2ɥiۥ`bIѷG_puR/-Z9Q5qII"mV`IPacU 'v"(#ƥP~l۔y+'H*!#aq.)c^TIF#PAf!쉝x HF~WCo݅wBMe6XT썀I2NT]\ɧ3 ĉw䋮}xz]buRPnBceѹ=ARc.瑪J9 ^p 8zaUZ{H%^Ce\=oWVU!&%"E~a#*Ig$#g>9V3ĊkW[ %ihp$ $ZIt|!˶@n1J]$WxF=EЈ!HU4;y'D*D;A@19#b~޿Rt*x[Uu9 *D f#{eVˇ޹~nz/up7m/E.,#jn`]QNI329q^8U4"Z,,Xz8t Lj]E  Xl#[a=[~]Z^ ȵ }G{؞:Lm!^?[ Lk7qٯf?֐\pz;^r Cto?7t72)_UOO +e59&Jbq NPW,3L#:%dض!A>Hp0aZ\Ktc7GL' gv:{@6LQfv~WȎ@jis~H&n;' oP8]=7OcDP +{}Y gSw(6s]: +PD$}氣ʋm'q4N9WNFcTDY]2EVN]:J$IL"Et!leP#A$o@IO^pH SpJ尉Ji2'19o^f٭a6=T9nQzPgx!U|؄@ +lS/S󪔲zctkjͣ95xb,H(0K~I }л>fٟd~6=}Aν'Zs7Lkw}6$.٠PR=Mql۴d+*GƮ6AZ[.,QRlU>5PY/=>!)!]C&_oH1f$4 *KF'RD`@IF: yrL HDw2.T:*jp`L}:_g!g7Ngg6n6P;vS^,r)ֱ +*O>(ˈ夽BɇvFF* Qp- _X>5@[U(%1Js4/+MC6IB#cІxJL[YO֘ ;ȐQz2{sahLNW:'dރp_:3Jv{sg nfjU3#FWS]9Ea9[s5kO"뙜3G@X@| 5*Wx9/Uf) D$rzmVDSEi beI8 0oDTP5.TyQb.5$ՎvB DBjʼn%; wltH9h/dUv5=˶2_,/>ވ@ө&̇u=N>('}@AI)fM:VCp3r9>zZjW=׬}#^0ogWsYHIڵÎ |6h醗]# DZ&[Yls11°D3o\IIˤyΨZ\H9OA >׌*`(3@O?#׿/טFrQVXT0ar_C4뇭#ѐ{t~9/wv^kƝd<NaC `Kd&"^T6;!lhͱxsb@!% %,MYCe:ϦcTҪ"ԋm)QH ++xP 0Qeׇ_? (K !2i4;fjv/Wnl +i{i =m3:ST K1X^8?>JKET%Gc42 +iJԕ,~;@vqJR]邱]f*.8wlʾK4O /H!xb*nKTJs/ Ŝ/|.¸B*&$11%{Ӂb;_ 櫉cY +W2Ʃq%<]HLl ⭟Ź.|(\̫Z +bc(Zk WHUIo']t'%Scv=0v)dАu g6%7O TvCxKWq!_c`|8 ++48yZh?}eVJw-kKo}`cb F"Zkes:<4bדY.>((47~!Q%/%Yg(7*9B-ֳⳕ!xC gWg>lgcJوڇ3_lDlp;H4pi('.KXn6/L>bi0Q)F ZZ즜o:,0!|   g'a,0#|x|dr۾cܻGױN؉$ݲ{| CGs} +S,:[O_I5*٣%fJ4 &TPZ'@4uUpּ31srsn—'[ R^̥3prC5V{1ʹVQ,o+bb&Eï ʽAt>F:1$E`}A| M MujV}ɯlA-ڭml & +%gL=I:D4@B {f{s=w"!XZC 6 eܰy4ˆy +z_xN4RN6Q WgR,ۣU)wRDMSz!!u͞^K;HKe>4]~:w rJr9 4:ncd')Sdz~ )LNrJk iI0AK~<5;ZrOFUh1bC$6bLWSvY׫[ONWJ[<$]H\qYbĽrpM^vt~vv;0/qd2Kׁ25!K,ZK@@gSW3UAo@X-_&+01IN?<Ljiu!,qrru! s ($o-u2|Ww'jǧܷJR߶ޥ1EɰCT +ꟓPZ ^h(,Azcf 5B: A[75E1ZX6Ԧ * jR}Hu q!}W*;n\{<@vYOWec3HM%#7bvOZ_7Π|޷E΄=Zz+zĵgyٛ) SLg{kq86w/ط!O`ۮ ~tWDp3IA8쵌+~q& x?Ï7ќGֹ +8 PG> D52"Lڄ !!@Rv%{]\>Fvo6a @tKa,ILO#Qzaeo eOeRg7{ypiwZT&M^0sGsAՕB"A^hD?aTR<Ϻ@'Q1./$(LT]bsWC"-9igCПUCB֞LFmcc|;{Qkӈj-yx*o*,lMٶRikPO[s8Hח-)m}S:JwGU_ug -ƫFG|pMҋI)-$j;I[Ki0l ?G]: 5?W@YX +(N!'f][J q<W+Mio><Q&x% +V@ +)V!hiBqaAҦ];/gOgϞޕ{`{R%v+a.4VrCwƛ2Ȍ,z9Z(Lk +u >P͎O ..tb_?>XoXKӜ)(\%/{7n#ZR!S<ydJ:WmZPuxEH8uҦ#e{hw邚 +!C.ӔQAxWf@7a#QƳ"N EQ?S&W,@LG@*09 $ٲ[UO#t*/߃݊Vm}mͲM:N+F3<21^wEeL#ema=gm1ź_H{QagrHG<Rדij>Y*m)ʶ>(uhƾn(_)ZEQeE9I׺K6u9:ޕ[+$[I[Crŏ +˱`Q+2|BTӿ۝̉wnuZzȼ2-'Τ1< f)3ðTGfA۹ QȎM%A=lzG;X$Əw<5&bF06C a"!#e)uwW6s}fy١""^?j˺o!;f> x&BaU,%ԥ"wGȖ ڥybٶ+/$/&`D +)L*EXQ'nGATu3J.) oLLshB#c^Do 9Siv;{xWםs=~mn|-1;T6ܰUyTY OΏb64/ĪHʯg+ +N yX83N&6%w|a ? Ϟ{\~9g6b}*ݟu?SݽY*&Ъiif_ ~ѶBԆɪ}D|*\|[YX33*`:^'3^L]= EdBFMkOL,Y;*wcMR.\Cm(kߜV߬fk/$GQcK +$>$vl6<.u ʠ_Ur?r?▶ y5-o,r;VY&NgC;V "PZAInʚWJ^p?9]L]2oD q=VE׳~yŻJ#Ù?L.nЅM^y͹h0cѕv:qq +) 4cj񕁜@)Ř"м͕?_=uԼ E%QQ^+,|}z؈˞>7{˸7;pvӎvn,GT."Sq :yGifïsm,$݇qK!_S]B:JA5!1o&y5Z(IFۘlDJ7qhDx6O̅*Lct7fz]BSp0׀L}q{q`?*Y zntX\eI_fU탭9 Rx.rkT`q(zCb I-Jp|ָNW\ʂ?e ;y 6*8Ƞ +2"? +*+)JBO0 f2٧=ϫ_ Caeu|p|x#ćq?"F[ ixN=&I"X +_!d@8WZ,b5yL:Ι +!&+T\G'As`3Җ\1=O^sSTc?~1=) Uhcvet;< P2 -,RNh-L@E*Ƭ ^1I^-T> "w5O!0 $8yF;j9s.$!fgP -{׾t9i6R7'֡%ank 9E\W&qLR H#&]]!IO +Mj֌+ިZ;'&%" Ȑg~*g+ݮiDQ];煒eS=R6vdr=4|q_5&SNj@LTKw=>}7;IPqz2NjQ:J.Fz*?p1HP&]%DqP'x#sTW85`CAڈS|.&0Xe90VD"tr'$I-b7C+EC][qǐ4̷-ENJ`CZCc1%k49*Vj4: +Lb +Vj7g^sfW՗ pn_%ӥ_ +9@ߖՍn⣻5-u +NI\W'/'MRn"+~0T$F[9dkwHl !"5uwqY^FÃ}WW|*u9iqP1.>ޙ+G~U=b58ˀ3j6*p7a,XիMeM&RUYUr Z*&zOI~ +f}(%= `(@}0Zo:)=[\T4ephB^Z3„Po..8RPج'QqT}n_73"ӷ 7v OmCEҎ3C<"\T6*9RӈrvO)gP+LJ&|~Z7=N<@A앀goon;L0wv:h,SLLg8Cg>}([ jA+|m~qH{e_;|NWӷWt=AA +LI)6tQsUJ "FBQOzQʜy3Q05 N16RB")0Ij{!{{^zGAs8-ǧwlZKqhv +ٴ9;(x?1^jC0,8BT5J*3Rb VL竅*% i:!B B)kYKqeJ +Y1fPoN})RHR2x:^hޢ>xIf6+;puɠU \5{=jϤKcb:Fo0[l6~LJS%5Dk[`}7sT]m( cAqW#AN4o HҭNXTe\,|=@ˀwr$lQs 7)Xuڵt5F&Ʋ9etmۈM!Сq2)JBdzS-Zf}_f?8{W_e0{O.:nC$ 3 Ӹwcߕ9'R%o*e1} /ѵ6,%8QBG˭-%%8wfRIF1$RBc9gW(W!}hPczٴ +HŴ&KHTkGX4!MsDBdiRחBP@0 -_T,Դ*yH0b(H+qy[\HмpAJ WNp=]l\ +-¡S|v}p}dT%q^2<&~i@{~7v b]m[;d$oC>īSlܪ.'ݚ^]˩{2u=!ęRC e0~+]yRsgܨŻY6yќ=.K]"|QRƽ"NMCbe6VzEj ۲4^E|S<3XrӶ*o" +QU1i' 'ᮁxs;-uI cm9Nx}=62w.EO`ɭѫR=2 @ 91LU5]TGc)(ꪚh~0qQJ8 L(~ +dk.@"/nbVtDz_fAx>6>;ـ|gh-T2po]ĤpV/U;KU9A*m _2gv֒ eF&|64}gl=5 BJK14/] .P:v,;|#ha,R!/-:I3kku)Aӑqgٟe?MkWK~ݼ` z> 1;Θ~.'h՛\۶PR{,;(@@XJvlpРVp& FCi n +f s +1È@hQ)SC[ f''@+`w8ۜQ6t^_cgVy`~yB!%)t(фMê>qQ[@Vxy9T)8kqА[ݬV T!f*GTB`8+h>JZJd"۾7?0wkǯkno־4ڲϞ3sj3'd$3C@JU%)R**U!0bgnq\ڬֈ4QQiEmR\!6"ب^{{{}s{} 2"g` d2M 5⢵P[j;t ӨhMҠ,y\2nV ZTB T/y'I\ˎE1G(S]HBJSFbO+E5O;K/]YМ:IֵXf/L;[U{֝1OeGLK䵃/\`/{_ӅJ"9tn iy:2/޹[(oֻrP79&4J^\XJmAb\y|o&@%eEH 7gyԺ;MN& ٌr{rPd0R׭YRU^i|GƒSڢ1놋89>B%KĈAYsɠjwbS)RrD81H1B δ_U<;u=1akgk2j*ӯekzCxҸiM`.{_6es;1Xh&]ϥN #jx6/6Y\ԅbhoޥ,:mFr뗤T"~.KFdy+ӾVs7췌']+]w.Y\^Y0☍qTi[!YIտog`];~sPN"gdfwdΓ'ߏ,SbgBMH"iZěE8xt'$h_E4d-8-^*y3(9wA7EUťYgH)ɦZ^3yqو[{\]=Q1]l*j%k7K=oυvy1 M+g ڽ3{`[ާqG<"X[}DQJY1.OnZE{L8M=,eXhYs'I>ڻ>HG"9Ր+Nv5V0x!-9YZ()6e452T2-'Ϸ1h k߄?_.hNf)rkMjq/i-W3 +es"ŘLc\$=fs.pɩΜj;iDb6-Vx"AEk4ўEKΛߞpzm\h !&J]g\ aYX߂pGp\ D#;SˉEߵA |`Z5|cYϳ9ꩼ~K˘`$sqVz4,En)ϜΖM{k]eNncb}$57և҅ݪiYyG̹{54O3$/z/ÅAUڳYΓ񚞙N3^N_KW -zfod}k?%0kۋ szZ_$ƍ+XV%- 㘢pWC,9Z8);I +R4Ūͻ A"/Z6-c%8qYg7%ެEu)ld&+pa<2nY,)"^ KPź 4IIsI!&(D(x$|>S_dcǣ1"'H-$OA0]x +u`M@1f\z&+1BHHJ{±ƽsڵ{sA3"ug^(5~|Z¦VRý\{/fZ8W0u wﮰ?Y$L;ٰ0 }LuVan5"3S!ɀŷ !o U֨SEVP$ =,ͷ2%y +ex]mv%RW%?sUlKI_ZrD޶DueҭΠHר B,,E)b1GS{<$3EZۘwd<2>ʯV +[*sGH–B6H֐SS5+{w +g󌴤*FNIv]AzՌ" Ej~yS[-Q9MV$`qၱf~ċx=GDxC]KpJ S4dF,*mzcr>4Yh'X +;=F!ıLo>#X]MtSUga.+ຩx Mn;--$uNkk9[nX&Q6Jj5z23Z/Fc2MFPB}Hݍdͷc1A( +uTޞo7\µN%iѳ8KH_グ]c#iV߆߂.9ud^rGqYoA&&23ǽ9%STtQS(sJѰ4%pڪNȤ*yʼ dͭ9^jw-)1lSj/;q"qG){1\juQXu35OMBɲ撶E\$/o7`,1 QmT|tkQ|s[.(sw1fkZU3U%ϛ_"(YĮ<83:Dc)kJMj%gjp 蚦ǘ|߃\:Q>?׵@|wQiF~+7gKU+io!k--l˽7i\0K5ϔNccc)I{&ذvTv/=؁p97ydDcGrL8:0$0> w_ZYOkL2g70wNu9'#F8%\?f*JQLkMUU,uM(Jsq'#z5'UxCRbҷH5lJ .gi^_^xհ n1tzJ3JZt:.կw0bڄ9%b<<`a/s{DdhN]4JgV' ۫ب4jJZHI&ڹ-2WtlتXsII$ǵ᱓\jeNMvb.HqGbۚIJKJ-KLD|dIErNDYd3I ڰSУ?+5.(j]5kJ͸m:Rz*-CH4W"bwIkuoy)Hǽ}9I7BP% 2geo-4<`(eq.Z*< 0!RWg3}ohQ߄(7-E 8b2kl,%̓VyΰcYDDYFgU / ^@1YX0Gho2=S 5s$C) y~ XB8l +9rc}ar$Pi@ 0TPC33@{t:HhO4ʪJB ./ľr_󜷞{뼋{ ~§/qџ]|?wI󒟽/K.{ٟ^~_+_Wu:-/կ?w︆]]C{~^xns7?鉛ӛ#/ԋn|%{Sw;_zKz;~?ݽv}?3>7z{>yxSFƃ7<:{/_;S}O陇N##w?ʟxջ_^3?뇟|4yI^ҏ,[y^^'<;ox [+O'zz\o9/[GdO~moyoɆZ?!NĆ ͬ~p +p N=Jd óp|g7i80Sp"$$ SQ8nDH +Ga184 +!xp)΁s`lÞp8`(p)888  pn#pc!/<Gw`lnSp K9.§O3pN18r>{sNVp 038І0?4 ~8\L~ u8)GóOz랐>>ׇ]|Mw3aY?v|Oa8j3'߻y{__"!ߨWLK>Lh`{erϴ^~'y^k}vۚ/i;A%} mdRdM0Woȿժ|fΉi&3pN+_8>e֟>{C&9_<69ߛ]; 8vVx䬞.ug}~^Izd9@$䜳t7C8Lo#'?ڟv˽[g~uq{o}p:>=:nwZZn^~ Oկq.i%P,d +XBvz쐅ǯX=V$ta_ uo}e SħhH]~3h0FCX!22$ +endstream +endobj + +710 0 obj +<< + /Type /Font + /Subtype /Type0 + /BaseFont /YFKLII+TimesNewRomanPSMT + /Encoding /Identity-H + /DescendantFonts [711 0 R] + /ToUnicode 714 0 R +>> +endobj + +711 0 obj +<< + /Type /Font + /Subtype /CIDFontType2 + /BaseFont /YFKLII+TimesNewRomanPSMT + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 713 0 R + /DW 0 + /CIDToGIDMap /Identity + /W [0 0 777.83203 1 1 250 2 6 500 7 7 250 8 8 500 9 9 722.16797 10 10 500 11 11 443.84766 12 12 777.83203 13 13 563.96484 14 14 500 15 15 610.83984 16 16 277.83203 17 17 443.84766 18 18 389.16016 19 20 722.16797 21 21 666.9922 22 22 500 23 23 443.84766 24 25 500 26 27 443.84766 28 28 1000 29 29 722.16797 30 31 666.9922 32 32 333.0078 33 33 500 34 34 277.83203 35 35 500 36 36 556.15234 37 37 500 38 38 556.15234 39 39 722.16797 40 40 556.15234 41 42 500 43 43 277.83203 44 45 500 46 46 722.16797] +>> +endobj + +712 0 obj +<< + /Length 11 + /Filter /FlateDecode +>> +stream +x +endstream +endobj + +713 0 obj +<< + /Type /FontDescriptor + /FontName /YFKLII+TimesNewRomanPSMT + /Flags 131076 + /FontBBox [-13.183594 -215.82031 887.6953 694.33594] + /ItalicAngle 0 + /Ascent 693.3594 + /Descent -215.82031 + /CapHeight 662.1094 + /StemV 95.4 + /CIDSet 712 0 R + /FontFile2 715 0 R +>> +endobj + +714 0 obj +<< + /Length 1250 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +46 beginbfchar +<0001> <0020> +<0002> <0032> +<0003> <0030> +<0004> <0033> +<0005> <0037> +<0006> <0035> +<0007> <002E> +<0008> <0031> +<0009> <0041> +<000A> <0034> +<000B> <0063> +<000C> <006D> +<000D> <00D7> +<000E> <0039> +<000F> <0054> +<0010> <0069> +<0011> <0065> +<0012> <0073> +<0013> <004E> +<0014> <0077> +<0015> <0052> +<0016> <006F> +<0017> <0061> +<0018> <006E> +<0019> <0036> +<001A> <201C> +<001B> <201D> +<001C> <2026> +<001D> <0055> +<001E> <0043> +<001F> <0042> +<0020> <0072> +<0021> <006B> +<0022> <006C> +<0023> <0079> +<0024> <0053> +<0025> <0038> +<0026> <0050> +<0027> <0044> +<0028> <0046> +<0029> <002A> +<002A> <0075> +<002B> <0074> +<002C> <0067> +<002D> <0064> +<002E> <0051> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +715 0 obj +<< + /Length 30185 + /Filter /FlateDecode +>> +stream +x{xյ8֞=,XȖߎ&8$!qbb <@)BI(BOdJq9-s()'@q_[#;߹~_^gf{ C@kJב.p] Եoe.w@Q͡{}ܧM i=va>im^{*(k{nA&^s]33@Zϕ7;~U!FC[ !P8 P85p+ +'pbpkễ7`/` ,Da6< +F\"p) p*`9H>,; m0A >\)h$*(0^(-c0;8}+tCv pā\T;n<}p<^+[#EXQ#L$p1zF2ZpQa*,5p<{-8 ;,>?s`\ |98XK $>8 '̓\dPRpX(@%tð~' `S\ņ٫p ~o}>J?ȶª3@̀e65p-|e8#]ϝ.NE2X` B|gx n m|LGLy +-!QXn;~xWUtc·S2& {vf'{c&?/ at8 AJ+&glDFfbd`~de-zgɯMp  各h^ Mp# p/x +wc82x^7x/ދs|_?􏔑2RAHdN [b2ۘ!fy9ȼ˲s; |WxDD'a20'2@P +F8< O`/[Y9XI q.UWcF܈po[6} 'xxo]?¿ @% +)')&m#K%l&I?$אd9Db\T1=0/̛Y&ʮd7obsn{{44: Wek8e8|/#&_! ~#exΕבwQ1v dwf$33 +O0sgf\I^2&µ0PgG֍+ȇXއϓL1Лpf)[h![qZxZ]1+n'/dt p9s-x!+7}&Jz8 *IL`q i\L,x(#㛌 3bq)9M:1p Gks5 _R*́v5ցYpfGcsǹp<$,y Z~xއN0< wB XuP +HEmS>`E Tnv p2< +p%`u*̅:@+q6 @0£A אVh +X·QtANIk\e=kgeztaUrQnݲ9mbKPEz}SĨ4 ;:!=S"~JgS\e%%-gopnJ^'h(In9jILwG^ުZYlK-vH7qwОswG~QwNל-9t//Z]lu4g{jnR?\T 9X%gPD.du'%)ss.%ӽsu2AB7}s#F*yaYgܜ=x2Nj*ⲩwʵ_nRKgfȱqhaӗFvύJs{ި$F1Lpݕ7bKU2J{׍i+m;rKԮhW]Uɑ+۪#.F4sC"tQ޹PBHm"\tI(t! `Heu^GaEC ͓"L,(HklGfam^(†S͋0;Ykfj 6k-`&¾ 1!vL-xH:C0)l,O7PWWE :Vw2]]]у˶]ҵ9QV% GŪ^^/Y# +5])K(VR] C|O|X9©VQr2s͹םcɱq6\_z'7+ KbRr~UubLkD+dZ'Zų5`oy#}RkjdPd!JYAbh! v 3 >-tAa' M +Zwop*vT, DWQ!BwkL=j(c>њx7B jҐDv YВ]yiPUTkm<}ͤ5yytyO"VȌベ3*xSu*7YCc"N&SlU/R/^4 5ҵ[V|7C0}F"g;WQ!25yƥYV|{ , ! {('I펒'?(=2t(iMP'⸘m'NB3&@GʆBmmLMuɠt Bn Slۥyٹ.r.G)/FZe>橴eo0O{<^̖'7vWݶ[\ _=؎+,3Py* #nMKOp_$n 3 f03Qki4j5Fmk$-W\Ol`PU L}MmB"YճfS墧<0ߧDuNXi>r 6˅,0(F r,fC rFB8hNͮ9k{=Lbxr˩IYV]§\)9h3u]@wx<^L9 1ƅI=c╀r?>8#L^OJ)LsD.mDȊ#a.P8 Hɏ˼<4@Ba.<!$4{T8>1U<ĭۏ9R[Ԃn1T6.1!_AFFZlKZ/GE/"ثGz=$vh + s%r}Q¾AFbۆλwF?">x$|~H|1p(KwqOS퟉/{8i/ +H$Ai82LA' z$.l%1 +9G֖'j.!$)}8qyu;qűy.n=ʷ: fχF{fDv0:QTo]}ա땔Qȼ]ShSsS3c\P4Ҳ (OgO|,]>Ɵ<{ߺs(l\V[J!n#+dS^_9tmQG!7b#@''^&Wwxsٻk([8,dLt\`qH$`"0XvzZI؍|/\C.*!/:ԸQ#RU(#(ݝ_jh5䵮Hȵ]-7;!~w 7C+1quD}ݡk|a置]]{dotp2t}йʹ#C T8RD +'F\F[ Ȣ,Ke^=rN'ӲU];Q|ip|$M m3]Z˘#\bi!5A7.#` yڀ` Ơ8C[Y!r_LdzٓTjf||@<<kK^|m1cE; ]ڹD_:/>JsFŴ/)Ӧ FYfݰ1Li^14 hil: )]⩯knjf*o}Rv ]z/nnK+=T}M$}VҢPUp,vi4J^@ G|~J5G)RfYXjmiXkytY,ŘeXHǬ%n[/|"-`qߚ.6a~ڜ/,˚7539Dn>"pc8|Tq H1J:/N3STICjf:3iҼIu:y>%i[[̩T*N6'oUzUY=񈪪Q˟U NQf4&93+:Vy"BR vCaj,5KoZ, =o:$9_l?՜/Umn\/Y.Nhŋ:QUq,U? +;d][9/)S8)):}>ߌB x.ttu[Mwu$H/O/kNkf.[[XHyƶ?&5yƺ?:+gڅюY+DsNT+V $-&3o`9~DGL_Q5{sZͨi\3i0OYŋ#KPvb;ixڻ;d>m<]U/>f[ʼnV`bz?L+=2;6Z>GҴvJ/)YV%Ye6%*|3BVŁlWWP3ԥnj}L +ךL+uz]YEygQxrntx/ՄiG`-)\ǥUWpjj`5S`M q̝]6srrIS*əBv,Quk[kVJH,8_57Zb1%8_&'ݫXpf~){m60ßt=83f44f4py\3 %f3~:,9n#6#zy'grO vc=؛|lr7n܃=`n py^(ǢXCo.g7[k-YvLFagr\㘰ƵCmR):%'z -1`DwbODMa@F%1+/:&|A̸ésWRE%#VJI)Ƚ ýt{̨E?ۨ<}.XO"{dl.K-,U=m<ب0j7jzRlPUkF̍FNTWWkk.ABėЭzOj]F<|hiH9^4ktPf.q{lXR0$5񓚕1k*q[_,:uP%I]8)>b퉝qRlihΌR,jeTKh%®eDع0*gIayoq2! Pf]N.# ԶFZhL#b1}hjiå>vN8ht ݯԨuzFUubΌ:isu0;!0keUӵTL`y8]4cr֙7kgMUuq[a^ZY9sRVCX{tJtr&%P?φ<%nE6,% O4 +c%K+ 0^M+(@klv֚ͺvDU:WlHcc.k{@0Ň(Q7bD^s<|7O],?]vexSBM8%ÒH :(}[ -.;:VcG!3=ͪuEi9s//^)ht>ma?sfV阿/*b;ipRO"뒰;\n۫"_|?otb;>ÉMo%3_ꪵוķk?hnZǺIf/ԲW5*f5tԥPnrN}VGPnY8"Vkø6aE +`w?@N3ddl*sY7hJ=QMU=z ]'vVs/w.pwG8u$8HZnq. hU(wҢ:SQHYBm^$a`Jwbu "RhE,s:cQƟAuNj&G%XLf~.;cc/# +]_>TH50JPHqWgz߂ٮtbgtJܓ5MujX4KV3uSgƳ(/ꔙU- vD"D8Bhh +.Uiu-sSSl a(пwn󷭻ܻB=]evQ^8vjT4ۑ|$HvB4j4LIYI@Lj͔N?c"DO`Z/X!G%_8uЕN\űű4j4ќלtgO35]Y55mdM'}%?T.]VBOu-7Ury}3?sNL=g?yhu{BT.9~sWJ5+d*ˡz}U#74;D;o\jFEzn}8l +༻]]-Um>p[܉H%; :K * uowI2 29 eL$h%`@:$YVt񻚽94Hid pYI5)z8QTKЕ3ZR$\%t,הX!2^YFD/-W|(/U5;E~JzxZSqg>JCmZw&0iX\ +Gpο,f`@]3 +ǚqRQ iutA8DtOhP&TЋYN[bPHi,=GH|mYu:%x# 6nU?ؔ?!ei +S[2v`|IdEjU3󂙥3gy6mQ~~;-fIn?$/$vH}60}[D v ɤR޴8qرcc:x6[B@>jBYߒ$\b+ѓ*Yړ^l(wWYM1iKPʥ5՗?T>LBF]:σ:b5kƓtiq4ǚ˗blbs+wCΊ;C !PK?W~^vűp,op^—+ FoX]¯]˼Swʝjvqjkk(bBQ:¢$ala{G; RU|JLx%DE<A0>;fRJL&%%>?}^ *c A["cdb$"I@(!C0^+@Zbf} 8 'y4L-?K|^+(*Mh4(/qp͢RkŇ$ǏI +8]A*)g4TA .MP!!&%|ٻq5'N"&NR9;t0F3-{i}(i>9ǩ@+teQϼ( +4zPgR4x .X2T,+d\‰ҴGin5Nf|qaT 7Eӫn|.}+o%\z(Q˭8(;+OJ(JL2'Jg2(l8INp{( +%M&X/?'`y,([ @Y`1]Wt2t%J tE՗#2:q3o _|]:eKߪzUsmߎ=#OVTu/U*< Ί@M)S@B!tSof(A"AD!9,!c'uW>./Kn&-!\!il 8Tb*̜% yLbjMMǚA +<CD(uցn~>llםbvLv*~ T}ԋ/rPqg:;R'ZV@)OUuUTjшRMcm8UezjzMRc;nK‰%hN5= E1 +jfU58KyCؗLC(ѾP]h_(g;"U{QeTU5<&H}Y MmM54Խhm|p^P|q +w|'E_BƗ_}kZr=Af(T>Z| wtZ-vIPJγsC%]S}-"5n-eV4:U@1N\L(:^p_i? ЋT<Tb"҆գʲa_*Uҷm48h9Un]|Dkdujh\ L?t:͙%,j,l7bsآ9ͲB,} gÛ~c7|{@`Aa up))ei^/*]!NQ]a6ihFJZi)=;B0baЧC}:OT|kxfzm|hjoG15E;4͢Fl &tJPAT[aküP k -HshJl.2-0/h\ֲU ۍn3WxnHfMt Zjx`xИT5<ZER ,K-Fҋk,G 9ķƷǤ||wSDD5Z+iM4A1kªnKCC}pVzԿ  +- JDRv)VȐ+/6]T +SZ^%~)?3"yֶ ۾6̸*NДuBV>ͶBɬ8>t1*QƂ*PgyF b0 +&2i 8]5eљ\:3 ΐB[CkV]舭_ +н80H#'颗ufKWSJԋtdIWF-8idN{%s:D=`N* +Z˜n0D|lҐOXjb?/$u_m >v'qUNnnjj.J$;=ɂ'Ef);בyĚ.XsC8ǫg8IŕTn 9M^hq_Vۂ%^9cmW:\V;.++KT5Tܕ\>3Kxkgdwzh`z=?QՓZl(Mhi,!KAϦl*MEu-x]i)JKJ!J>:DF/N (&A51"a&zEB$VC]cf[g "1B{bXH]¦R﨔yXPs^HӯI[WS:j1:=)9WPGy +xT,zgW((AS(F"0ԟ(R;G[JҨU6 +4{qicwcFEM5j5rIR]s%2!-&sXxC|\`: V0;P4hdH /X4cD+S{ fh)@ԧbݼ)h:i/<6sE3]Z~5fMa*[kLg0he덓[VEXqrK@LTjʹ}-n6 4H_dH1:KC18k\1F{1"{AE{1fb)BE:?ɦrScR>y߈96*Ygsy𶧟uߎ7ߏ6M7~_mTK֞\KΉۗwKtژ rnx +vlϫ#e\:4A2ހ]茒Jl٣v[6dclfd[`:ǫ+Vh +a}|"iv|";5iPGQ MEԌ^(3W/sQS]:ӉT)w0\l2ZIg₅m}f9I\>+6%͌Zm 5sXx C68S?q BRCT2 P1Kp!y! +#LK a`j(k2K2Y~I.Ȍ|ܮF7Ep&vN{~]p3fjiFg:c1ULƧX}S}:vL}EަFW}+% n[ \ÚؾTC}NL/PCŰذ*Q.ZW>':r>Q.'Kk2`vK 6!$rY$e QW$蓣~$JXkZ0-?UM4͒L$Dz9tnSA]QO:;(`͞\͉&"I7%ؐʜm.Bl}.1 н$+Q 5K='EΡ)E˶4 m[\arЯX+1T#nIT$JT*G)3 + "#V%ϟunDst _<˒/%aR?e~'ꗪeiN{=Yذ"/]s*o-20o8^(%.ql쿅?pvnĦ26/2S")&yp ΃f!/nenp_繇|y0c06g.%X AÐgU̓w8Ԝbu`uXLJKsus{qd7J>">Wڷ@YA+Og!31p&{g+kۢI+9Q AcX*\**B,lPIRH討+[^}ZJnuk[+ $X02Z!8Ȏ7ؐ3O#$Xq;b2ٝq N$LbKuK;A}uֽq.1%.lPUc8p"I?sh/ɐ`}9UbUe=_|]KO;})y%ɡ\?zԾML'sObљ E%cKG-z]}:8zoy;+HR?q$1][YM肫_婬7Z=ɬ/\: >OяoX? +h6Kⷄ͉X\ =9W#Շ厄#9=JzRw4%=L^/Y/]/[)t :=Cm;wxF_S#Ex})?`6 +ޏES8VPXV߁ +.luu32-'M͚*.$\k%Ehc!f`YLYZJX$Y%l J T(Eh 5@^u_@/l@65!BDvmȻ-#WSCdHHH rQ_(5}aNJHö` +ee)SpYB I,wQ +2PF67ny\'en]zS_Kܡ0cFoΕa,<3)oՇ5dQ^j_8s)ONz^TYj ш6yKAѶB{C8X3DK"79)͟G Q. +{Be6)`Z@GJhU$%a<_R<{^x̙3'0.$uY&eKPw =չ + AOIk#&^Ҝ;1ͪ얢Ic| *yN&DX_ JԓC.\%aH*.2f)>ڔauNnE <@]m^S%+(b[wU-.V ҄QSp*TdWJ4>R"3>GCk~BuPz/pխcՒ{ +yNEtcOqCƉYZCBo)6 ! PG^^^Ű`[b +,Xnz*KE;blPiu´ 2L2YCeVaVVVAО W4 +5_WJa\&#~2d}{UrZkT".ͧCRݧ_y@u@GCq2mO;n}7GŻ<3E*~U}<2^ 8ĸhl"v6&i؁[?/C7{,`ّ_..61iI i" 9:M<zCږ奨:O^ɈyDguQ^mdPe!†;KaSUѧuC){^/l'tfUpHYj%W) 8,8a"Q-H2CF +Qʒ~dF a +`LJ +NZ {P~©3<{lTgV鴂\GޏvV܌ 0?gqGf5:9φ~ +ZZ$Y;5wjoy}*4)Mr+<_\\Z26aOx|»|Ab*jnU񪈛Gy 4YM#e.wz<ϹOSI$R֚jmD]e +H}h@.þ0ߠ!//sz$>,UGbeUdcoޏa Dl"^@MFML]QZcHǩa)"?+_/q?!3"ź` +%)PSRG#!$t⒀y\J"3H♘aJhz"lWgWɪU6]MҩvD:YcEE^0elv+ʤ9Na/'{iiFx==LOQ!Ν@逡[,|oy7v,p9{CS_3Y^ȝ{T[n8x#'/mmrK͡/t|v>N{l;!Bs)R|$ԗ]4Q][#S_q<Wp<|>-T?XdDR P,# h[4k +PZ- wuSuhsmgО~vdD6"Uai!AG-O =ʱ~~GNdLUCKJvXTRJ]@:P,K phKlCnHW`+|/ `J.LXVl?~7Gd>.^ R3"{l2-J|7d# 6"1nTEyIgjF}W*1d5nmgE8qz fP"=_ʷ{Myv;l(\~ 1Ovxm/ >v]˶?wm\{G7mqg[oݫr3#D圆Dxl6r wvٞ?g7R{@:4? m FbW9\Ů׏?sJ +?i'wRW˵S-BSx-q36%aer88G‘u;$%Tiab(1ǢtTӊSK%rq<ۯ דZJxAm8}ĸfڐ.kV-[ sS(d/Iӗ*0otr"/yx@ADDb uEݾw'{n$'XlNhze[Կ%hI.2 +Z5d +.-&'é:o V,wo}pmv3S× V~vRȚ[abC^@Mʩrv2>l;pr`r{̌tar)owj>·Z c[, wմhN"a$ϩja 0S!}%ytttEtї>VD2q݂~ Θe l\ã3sSУsBkrUi3Ƅ+۵y\ə^ O%f5sWÀ^7 $a&:S̜L YRIs@R"˄Q-oL lߢw/ Tyq^!{ߟz?BMȊ ,k)(d*B9E)~JE_WW>"eJE #9jNwZER/bS"ѰRf% |jZ + 9_|?*EGh$DTP D͏@K8?E-"z}l)}L .nƐL^ǓË7YpDokGęU%\n "MNǓY] Adq^6A/ғ@n"+'8Fo+4 +56F)De%:6%4>G$-}SḿUz[ P&ƽ"<0]i{a9zvݺڗM.ͦ;~$U;+*R&7W՘jMu3ڸqsYcqm5Xk];\[- ב|JjI}M7UHr,@2*#,)ɡg>CyCa&#&Zh=ʶ޾B4iJ 8NN~0y2|0YsnV!6yKn[*oR,YX1΀SKGDqs-qzYSc@yee_-XzSk y%K"pKܙJ-\e2MS0{Ғd̙ xffZ3\] ~aa]b]vugIQf|vMjk4 [ް` 1"B,j/u Ƈ3(Si0GOOU+Z"xuϡ"y^k.*'; D"]T51ሑHG_Dל 9j8`:l.ݐ-.Ն$@,UV^`,P\p!| Z* +P2,d\^#j5jZ]t!s>5 2 [BW`EU4Z]"?jyo Q1!,bYp1rPNɥ<ܔ)+G}r-GmrD\l^;k7Kk($]%(LF'wO@7ɤR/K{HQ"*H:30PE3?K%Xq+GEyo]}݇߸EuGܼuccjM/x:U/в:z%+*D)6% +M=_Zu]ܹscvZ[-5 +/ɽC)30g [,\4M -*E%PASˤ-=+Jk*EnEu\rƬBˬ4 PȢQb@(PD.ߊ#Α d., F˜$uQֽ͸yU4UZnѢ*-$LX1C'])cyAwLjhu&C}%_#OeHY)Y)< -!!^\\|}p\6a/>mڄk-^ap _Z +ʍ!ucl0CZUQ35>QxPyti0XY0o9`a,;WX92BZҋ]d7YSCVFo͛ˋP +FXy( a*D'ZA"ʢև x"ClGsrɬm_IRIk?z W>ooy~]m:hT8KUSbҚumZ᷾-/&w8݄~?ݶۯOIvI3Qōh~ZmA;< M6(X]^%9l6.9 r9f#r\#E51ndf58$%t)sd*p6̄5F0: a4KBy[ªZbd.dz"(\AZf1R\T +G)XuRJ)38՞RPZK),(O x*!<'zYONvdޚp8/NͽBML(Tp@oT$ןď?[.>xԫSrcndCJtԻSO}'_9n FsB*8Ad`PSCZ4ljM̧c&n(Ub)YZUwgRRX +zoz$ݵ5t|V#/$Wnz dܴO_v9?2qtO84U@و? N5j>?q̄~ i`,@3 he62_d.I^=$Mab 4{a%F +|,@#j` ɟג@%Y͒UP| 4,IwFX= I^;^Vä y y5l"Ifw)R 4F)c:iwnpd=K`F`̼
7l1b @-B/o } `'lM446Fp#Fs3}0 C @/ z@~33O@ +μUAaB?lNqv: `pMvl}!@lK}Iym˷T -07Bah 043K)Cv>M 7F$&Z׹q?=gFp$|66AAlh۲ړd|a?× pEz°wfV~k.=s;/IIRg6WS|޿{.*ӗoߟ,Cboᨸ"nϏl"PRߍű%c6u˗c5t~)+ƕ3!LWNjChu 6@е b*Yj18cW*ȣ/?N!JFQ\go +X dgfKo 6˺Ⲉ3hmgh ۯ͏sEX:I1no sKWҳc1CIz(=0Lc@f3BUcGdҙoG/[gb2u^A +}ejk"=Bfwfv%*!o+62y% lŠH"$+O%#N|}X¼:w[9wg{dj@v=SXE\!ORl\_I>W}RO>{*3?iH6NJ}WFt`_DD/+sW.ַHC۠ :`5rV`,h 4HbhtX :Ҹ&/o-AMkhiKZa -#)VBQ*( 1EiѧkEr^1SˡCkHIM^6SϦ|MkiIZ] ˡ`-ܐ2چ&XoK#)^OGgU>_+iU-_=A;tbi(ƬIIk[lđ"=^K{AR--_srVXbjzsO +AjOZ<͏%㏗J0Ez0p[(ٲέK1G\ ++#~!NFN8{csr 4n#B vxқBX̹I8|qp%5p9ס;r><|y@_@e\͗ěaavmaӗ62;^e[(z0LʠrT^UB~yP<8&u98.3f|w~᷁_ 6 0/dzb1_Gb 'O1+"sB)ܘIC~>VW*xIi\?j<K^sgAp-` ^hiZ%~ ? )|2rr\OB L{,{XO^$O1$O8~ڈ~? #@@>|#B ~?? $0>CyA|8Խ?tCy0\> !L^Zq I ;=o׎x -vwrBIq˸Bwu$fy\&nUhjq|&~pg9d^f/dx ']BN5õDoo~% | ">n8$aX X$XGCڱcC3!4$%uZH`"wk<%k g|3~癏1̈́%뙏@J/8,I|&(':P5d(S4 o=Rlwk_V\E"$u=9fw$tc9cp9xęcaAs Xs x3@yژgy$pyYa"eѱT[R(t3GC#Q0Gil:FGpYWg_?x5jxI:aL"O}6g05ռjc4PGt' ]ϣ?Bh4VSshjfCc59П]-ПZ3-a?%4Z}eHIǡ;PϧF>_@@XCE9h/C׍ՔzY%CjҚ/q1ZEbƱ(I@ +ȡ1_C!Riq6 > |NPWt(|w C K>\ +_ƥ0N|p).mmk }I> +endobj + +717 0 obj +<< + /Type /Font + /Subtype /CIDFontType0 + /BaseFont /QXKUGI+NewCM10-BoldItalic + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 719 0 R + /DW 0 + /W [0 0 280 1 1 591 2 2 356 3 3 591 4 4 414 5 5 855 6 6 356 7 7 473 8 8 876 9 9 727 10 10 650 11 11 473 12 12 591 13 13 817 14 14 591 15 15 881 16 16 827 17 17 866 18 20 591 21 21 501.99997 22 22 650 23 23 532 24 24 487 25 25 787 26 26 532 27 27 297 28 28 944 29 29 591 30 30 896 31 31 621 32 32 356 33 33 385 34 34 532 35 39 591] +>> +endobj + +718 0 obj +<< + /Length 13 + /Filter /FlateDecode +>> +stream +x +endstream +endobj + +719 0 obj +<< + /Type /FontDescriptor + /FontName /QXKUGI+NewCM10-BoldItalic + /Flags 131140 + /FontBBox [32 -249 1003 750] + /ItalicAngle -14.036209 + /Ascent 806 + /Descent -194 + /CapHeight 686 + /StemV 168.6 + /CIDSet 718 0 R + /FontFile3 721 0 R +>> +endobj + +720 0 obj +<< + /Length 1152 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +39 beginbfchar +<0001> <0034> +<0002> <002E> +<0003> <0031> +<0004> <0020> +<0005> <0051> +<0006> <003A> +<0007> <0028> +<0008> <0044> +<0009> <0046> +<000A> <0053> +<000B> <0029> +<000C> <0032> +<000D> <0042> +<000E> <0033> +<000F> <0055> +<0010> <0043> +<0011> <0041> +<0012> <002A> +<0013> <0035> +<0014> <006F> +<0015> <0072> +<0016> <006E> +<0017> <0065> +<0018> <0073> +<0019> <0050> +<001A> <0062> +<001B> <006C> +<001C> <006D> +<001D> <0036> +<001E> <0048> +<001F> <0075> +<0020> <0069> +<0021> <0074> +<0022> <0063> +<0023> <0037> +<0024> <0064> +<0025> <0038> +<0026> <0061> +<0027> <0068> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +721 0 obj +<< + /Length 4831 + /Filter /FlateDecode + /Subtype /CIDFontType0C +>> +stream +xXy|SeNhO%Hc"( :ePh)vK.I4mItoJiJWRZ(%AeD:踁^{:_x)0ws9yY#x{KR|YTwEI Ok"^^,"NR.UN+Gxi>ӼIcO?7m/GVxdƨ#5q㉚ĸԀI I4qK HRĥ"R"#بVX XRTbjTܹQQMibIR̋NJԤK(u纹_bҧZt&SD%#H%02ICv٭> +sMkj[2E*[7'H%dՃtD_2 onY㦎[;QqJ;|΍oal OJ|^kx~jw6]}ox37pŞՔ&N2ۜ/>BJ#1WiRXTbsl6b{3P6s_ᅆ͕2|Y./knFWsiLh<:ZtVTJeEBQJ^ +%Qd w98Z؎U2-%E-(vc6ZS>8˭->㖊튭Z(7$d;sZ;ŵd,*}i )(mkžƽGSŒVSlF[n ^6GJSdWѬ.OIqE%#7V< +2VHALo>A-׿ɕ|[zb i҅GÒc@>dZUW &;fiva+ݴ.8 +6u;"O QL19Q`Y۱ϩYqbf{`^Hwߠoq)aaŶ{*@iJzF4*ݠLǂgcM| +j$ⱮG %pNd/~l& zA3NtZLFEp#t_+(+G|tZ$jPjB7SŢY,y`iJF7*2/Y 6ӧ(2)-Z`_u9OU>薊YX_)M*e!lG{SSR0>cUMnjl7GgxU1>1(FN ^KVAo0f\lڭDP̆(]Yczl6fjt@F ֋VSLSdzn +kju tPՏ8J% +l``BoBٳ[Sudu N"R>|v(r +T]-h V vz7`[#|."2~[bgD~^G ]L.󰘅Dp <[mIՎR\@b3N-r x3LI$0vS};<Ũ7/LeyUťhٓmmB^LNsE&gCMGhT~HVj`s6nzx4yh[,~6|$H`Y珐dok֎lFd40d7̥zG>Ybyֶw>4~^SJ/[0 r^$l&?SrJPd-Gjуt&?ԕ#mT2zntQd +HSG)MA~__.=]*G\C?)_bWU8Akԣ)> 'J~9V_g\c۴Ah:*E#. +r&i_~Iab{o3oޙ`pF]?`j,1ڊރi&5T[.V*=YD$4X,fdJXv ]>r2ҳK ͈\ ۖwɹ@s I״)E#85- wı]aǙ,:[JKWQUPF9 +h+̸*5pE<ඥ|6cSYZ]R-ql-`;PV5|U(BkU5`mw +ŅF5R'~niZevIsC{{i`: +{-q^#+H %s![S3 n8Xf~ӹ4~ +=\sZ~qu ⱴ4|['!]jy`fs +]+R$,6!ۮتC2קY0Xlkq-{Z̭ffAl5~#kOXX^Չ.DaL * ƚZ>[(<@4X2Ү\֨ڇ<V*nhR UOR゗xY\@BWƸAyP.^nj+r)Bi l(Am3&ø\rk<(bs +o/+dgy$=6!ekUa{̏` &dxDbX MxP[24Lxt]'%_ixj|eլ|{ +fl.T~*a0Aobt+[b (֛l!Y`Od:)onT""-,|ƠAb[{\INu -|;z +J%NuI$)5Kz=y|򬷞!_Zs%%+o +A,-wCtx)[M5bKz8 K_f啚}I/k3 Q$mzd(OlSf\E4$d;%v(h+0oG؍ch)a>)<z?}~&:q~ȶ>TԬ2@eG{=[zv7%ǫd|wiw+E+Xݞؤv\T…T&?C L]۰@&k7%eBXYc= +XMmevƗcC܎S#;١ކf2n*Eґ 5إ||`cs? UxB94=6يNR)̓@dd֩6kVZ}VL\0>oR],8E I"{hPAZ C6KώQ`uJ@֒ca|Ro=~@ďߦ_S MUnE̬ +2pNnyURY +[eUkWN*򓘌fp? I?q/R=]a8䡰Mla +-) (³7 5C(a2:qWJc}D\\ڻ"gd=+oڤ"xP WVWQsJ5$ _]OS)'h\ߡŁo4e& violo; +2ɇ_2oB"85;N2*8Kn:,31NIKi?WdGn} +,$wOێ*NBX$d䄛T%o^')!=+y?Q?7<|zKtxjVroS/Rq_$q[EdQ&Ŕڌ5(v{>m>*ZըcC!+g[ǜc%5m_y E9L>@ƽc?vv[;wTx+,.>Bw ɿoUSb?S .b/G/ו[x (l/8h7loNکt3q% +n)M%>t&v1'TYqwhkXD{BYkIW\Dc1G!x>DM]W-=wƿF:U7+Qہ*Y3e=,?ٟx_'Sbڂ4~2Cox%=9)UG&- `ºs+N}{hgJ\\0:UB".]3āػyL1$EHNMW4\3wG:R 3\mu-~CRֆXv]"5zӪmNG>vlB/Uw'O +endstream +endobj + +722 0 obj +<< + /Type /Font + /Subtype /Type0 + /BaseFont /JWVCUS+NotoSerifCJKsc-Regular-Identity-H + /Encoding /Identity-H + /DescendantFonts [723 0 R] + /ToUnicode 726 0 R +>> +endobj + +723 0 obj +<< + /Type /Font + /Subtype /CIDFontType0 + /BaseFont /JWVCUS+NotoSerifCJKsc-Regular + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 725 0 R + /DW 0 + /W [0 310 1000] +>> +endobj + +724 0 obj +<< + /Length 13 + /Filter /FlateDecode +>> +stream +x G& +endstream +endobj + +725 0 obj +<< + /Type /FontDescriptor + /FontName /JWVCUS+NotoSerifCJKsc-Regular + /Flags 131078 + /FontBBox [1 -195 1063 880] + /ItalicAngle 0 + /Ascent 880 + /Descent -120 + /CapHeight 729 + /StemV 95.4 + /CIDSet 724 0 R + /FontFile3 727 0 R +>> +endobj + +726 0 obj +<< + /Length 5024 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +100 beginbfchar +<0001> <7ECF> +<0002> <5178> +<0003> <641C> +<0004> <7D22> +<0005> <5B66> +<0006> <5458> +<0007> <59D3> +<0008> <540D> +<0009> +<000A> <7A0B> +<000B> <666F> +<000C> <6109> +<000D> <53F7> +<000E> <5B9E> +<000F> <9A8C> +<0010> <65E5> +<0011> <671F> +<0012> <62A5> +<0013> <544A> +<0014> <5185> +<0015> <5BB9> +<0016> <7F16> +<0017> <6392> +<0018> <5E94> +<0019> <7B26> +<001A> <5408> +<001B> <4EE5> +<001C> <4E0B> +<001D> <8981> +<001E> <6C42> +<001F> +<0020> +<0021> <91C7> +<0022> <7528> +<0023> <767D> +<0024> <8272> +<0025> <590D> +<0026> <5370> +<0027> <7EB8> +<0028> +<0029> <5355> +<002A> <9762> +<002B> <9ED1> +<002C> <5B57> +<002D> <3002> +<002E> <4E0A> +<002F> <5DE6> +<0030> <53F3> +<0031> <5404> +<0032> <4FA7> +<0033> <7684> +<0034> <9875> +<0035> <8FB9> +<0036> <8DDD> +<0037> <5747> +<0038> <4E3A> +<0039> +<003A> <7F3A> +<003B> <7701> +<003C> <6587> +<003D> <6863> +<003E> <7F51> +<003F> <683C> +<0040> <5C0F> +<0041> <4E2D> +<0042> <5B8B> +<0043> <4F53> +<0044> <82F1> +<0045> <548C> +<0046> <963F> +<0047> <62C9> +<0048> <4F2F> +<0049> <6570> +<004A> <6BCF> +<004B> <884C> +<004C> <811A> +<004D> <754C> +<004E> <7801> +<004F> <7F6E> +<0050> <4E8E> +<0051> <3001> +<0052> <5C45> +<0053> <4ECE> +<0054> <5F00> +<0055> <59CB> +<0056> <8FDE> +<0057> <7EED> +<0058> <5C01> +<0059> <4E0D> +<005A> <6B63> +<005B> <6700> +<005C> <591A> +<005D> <53EF> +<005E> <8BBE> +<005F> <56DB> +<0060> <7EA7> +<0061> <6807> +<0062> <9898> +<0063> <7B2C> +<0064> <4E00> +endbfchar +100 beginbfchar +<0065> <5176> +<0066> <4F59> +<0067> <5E8F> +<0068> <4E8C> +<0069> <4E09> +<006A> <5206> +<006B> <522B> +<006C> <6309> +<006D> <63D2> +<006E> <56FE> +<006F> <8868> +<0070> <672C> +<0071> <9879> +<0072> <76EE> +<0073> <662F> +<0074> <4EBA> +<0075> <5DE5> +<0076> <667A> +<0077> <80FD> +<0078> <8BFE> +<0079> <4E2A> +<007A> <4E3B> +<007B> <4EFB> +<007C> <52A1> +<007D> <73B0> +<007E> <79CD> +<007F> <901A> +<0080> <7B97> +<0081> <6CD5> +<0082> <5E76> +<0083> <5C06> +<0084> <89E3> +<0085> <51B3> +<0086> <6E38> +<0087> <620F> +<0088> <5403> +<0089> <8C46> +<008A> <8DEF> +<008B> <5F84> +<008C> <89C4> +<008D> <5212> +<008E> <95EE> +<008F> <6DB5> +<0090> <76D6> +<0091> <4E86> +<0092> <57FA> +<0093> <7840> +<0094> <65E0> +<0095> <4FE1> +<0096> <606F> +<0097> <5982> +<0098> <5230> +<0099> <6709> +<009A> <53CA> +<009B> <9488> +<009C> <5BF9> +<009D> <7279> +<009E> <5B9A> +<009F> <8BBF> +<00A0> <6240> +<00A1> <89D2> +<00A2> <843D> +<00A3> <6389> +<00A4> <98DF> +<00A5> <7269> +<00A6> <72B6> +<00A7> <6001> +<00A8> <793A> +<00A9> <542F> +<00AA> <53D1> +<00AB> <5F0F> +<00AC> <51FD> +<00AD> <8BA1> +<00AE> <8FC7> +<00AF> <6211> +<00B0> <4EEC> +<00B1> <6DF1> +<00B2> <5165> +<00B3> <7406> +<00B4> <539F> +<00B5> <4F1A> +<00B6> <4F55> +<00B7> <5229> +<00B8> <5B83> +<00B9> <6765> +<00BA> <9645> +<00BB> <6B21> +<00BC> <9012> +<00BD> <8FDB> +<00BE> <5177> +<00BF> <5728> +<00C0> <4E24> +<00C1> <4EF6> +<00C2> <6839> +<00C3> <636E> +<00C4> <6307> +<00C5> <5F15> +<00C6> <8865> +<00C7> <5168> +<00C8> <4EE3> +endbfchar +100 beginbfchar +<00C9> <6838> +<00CA> <5FC3> +<00CB> <5DF2> +<00CC> <6210> +<00CD> <529F> +<00CE> <81EA> +<00CF> <52A8> +<00D0> <8BC4> +<00D1> <5668> +<00D2> <6D4B> +<00D3> <8BD5> +<00D4> <4F8B> +<00D5> <7EC8> +<00D6> <53D6> +<00D7> <5F97> +<00D8> <6EE1> +<00D9> <7EE9> +<00DA> <7ED9> +<00DB> <51FA> +<00DC> <6458> +<00DD> <90E8> +<00DE> <8BE6> +<00DF> <7EC6> +<00E0> <7ED3> +<00E1> <679C> +<00E2> <5305> +<00E3> <62EC> +<00E4> <5EA6> +<00E5> <4F18> +<00E6> <5148> +<00E7> <5E7F> +<00E8> <81F4> +<00E9> <4EF7> +<00EA> <9047> +<00EB> <6311> +<00EC> <6218> +<00ED> <6742> +<00EE> <9AD8> +<00EF> <6548> +<00F0> <4E14> +<00F1> <63A5> +<00F2> <53D7> +<00F3> <6536> +<00F4> <96C6> +<00F5> <62BD> +<00F6> <8C61> +<00F7> <5EFA> +<00F8> <6A21> +<00F9> <53CD> +<00FA> <8FED> +<00FB> <5316> +<00FC> <7B56> +<00FD> <7565> +<00FE> <8DB3> +<00FF> <6027> +<0100> <524D> +<0101> <63D0> +<0102> <663E> +<0103> <8457> +<0104> <51CF> +<0105> <5C11> +<0106> <8282> +<0107> <70B9> +<0108> <6269> +<0109> <5C55> +<010A> <91CF> +<010B> <8FBE> +<010C> <6BD4> +<010D> <8F83> +<010E> <8FDC> +<010F> <79BB> +<0110> <95F4> +<0111> <5927> +<0112> <7B49> +<0113> <503C> +<0114> <4F5C> +<0115> <63A7> +<0116> <5236> +<0117> <8FD9> +<0118> <4EC5> +<0119> <638C> +<011A> <63E1> +<011B> <9002> +<011C> <573A> +<011D> <8FD8> +<011E> <523B> +<011F> <7A7A> +<0120> <4E0E> +<0121> <5F71> +<0122> <54CD> +<0123> <4E9B> +<0124> <77E5> +<0125> <8BC6> +<0126> <66F4> +<0127> <6CDB> +<0128> <91CD> +<0129> <8DF5> +<012A> <610F> +<012B> <4E49> +<012C> <672A> +endbfchar +10 beginbfchar +<012D> <5C1D> +<012E> <751F> +<012F> <6811> +<0130> <6216> +<0131> <63A2> +<0132> <4ED6> +<0133> <53D8> +<0134> <6B65> +<0135> <5347> +<0136> <7387> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +727 0 obj +<< + /Length 70681 + /Filter /FlateDecode + /Subtype /CIDFontType0C +>> +stream +x|wT:h6UYThDXP +bREF4QQE셢F5$&&;$m9=~ʽ}烄ԉH$/ٵo׊=ۂf/X=lwOz +Һ %ʴEUի^tw7AHƾכ :vޛ kjWOLz%Pfn%0t߶}wEٶ5xǘQ3jX<6iȈbˈͻBF!ިǶo ߳cWGvmyS5G{mozm ܵuX^@H$z֫K]en(^\<(CuZ/r^u]N>䓷^{NY{nI׮e|thO3~׃(#祧Ӳ7Aⴛ&w6+no)6v<H!$D ;F0_]xk! z[ 7OM#d!8;Çx?'iDvݍp_C(U.Nx '}"]!<_Yb4bTG *%!ansMkĨ*b='"bć ɝĔw2b~WL5sWB̟BxM" EE Sŏ%F!#]#>aO&&9'߈/g_"^D/_&&9C:G|#xE|ߟ%/xKgUį߷X!?n'?!PBdK$[$%I& "F%&y笤VB/IyK؝ w]~ Q"?&QIz+Q>$}KJ ̕ * F2dhd(Ɉ$#gIFM&|0E2vdS]dBdSɤ%S{IJߕh,̚']?}w[vlOᤣ'8/ۮm89m?qv0` [Ze7er#MjQmCK??@#!!3aeiz^3oeȮ\:qָ%똄4EHZ^5HN\O=84':kzGw MϿb5hp9۸ğ(C( Kф!2MZZ|4ņ$,bv4IK?4yx;+n`-&auH(c( Qc?~8n9N4Ha B`-fMзU @O.1 +_tv?`sޏ f3D1}ppZgf&aOTr +J7P苡Wa7܍,m%IY]?*|͙4b2FX@:il ]`Ȱ48=]ZoȰrFϨ ab7`O@Ѓ?A_܀})hm?ځ,꿉>*￟  `M$>eRBC WQ}*\-+'ɔ5,ݳe]%s.*AC^wV[g KC|/c-VgcK{xfSȬ6s[7n&GDٳtJ1 So[V{2y$t5:swNICWjК3o‹i㦝YtcV/Zǰ? !w7U$fñ7m!0jIoyFo0=ȣ>.=>`pT}QiI_VBgjf}44kχvh8"͠"넮БX;χCMc з jq.>y0̗S Lwt,%q}[132yUx;v*5Ht>a#|@gZg cDISg2tA󷙟YAG(iuz/ / UFMӤk%ZM(s}XiNk3]cIXAy7>{ƣn";p/#\Xj8¹B,.<[t:KLm$RMXA \@׸趬o6bn0opN vb/tzrAs./*teP ?bj[w"Mi%˩STn428s؁߳~F B7]50es=чn\r4rѶ0_e09W~icƘ$Fgb]hNKislN%v´)Tm*22q%8i_\NWHS5 +gXKNaE 8k6*˜Yՠq0-DWf>H~-8SsIL7ҥ61x$i4+R(f8?QթXX-N[6Y(uDfꕗ;=O3,Y߄žQ akc[ CڦE0z }PK)$!#_1W✝Lt~UPm4fX|8?L~+nҕf3yT\8äSk+r81C< z $x؄w8RPrRtXW7O.H1l0ƪD#gFcEBMizu&ӦܸYL.rCg4NѾI{w2Ű";ڜU\x,'s,1IvtU\|Ϳ`' ;0v{)sN..4(zM9{G#uirf19~k07()aԫ|VQlt\Ao;0vXmwU6:Pͭ[B帠o<6*0*92162VPCƆF0 qG"z{FI qr1[>8wœkS>[6[an[Ώ+'4v{4;%x3Vq"&ΆM|PHͩ 71]f땂qi)쵃xV%vç,KƩN[d^#o]z:<9D/3Pc0fXy7T,_:#.UdbJ2J=骲դ)!)votƵFs$zQx e4z]l^Ov(@pz2zi\3 y,9,G>X$Ii,ꓭqȌpTyQHs:Ä&~?$hΨJR@K>7 +Jk=̻c- >jrCm"X6LDWgBe=ۿy:%˦m0OW :Vep"M!99 o͋П8,MH J liUUcsx 6ŕ7tFwa\@F$V?N/ +=(Ş+&euWn0}ŐW,KS.$ +|^{? 5&SF鸎έ>na-FZ),)KlЯ Vkz fe%9FcTp5Op's([a唡N[k*:)1b7v11 \^R#5EFn| OiJ]q.0dQ[iV۠f``ndBf}0KSΰE~ǯqY 1S=fZBmV18b7Ԧ#\.7ŀo]nv!ġ\d*z/ 0gXOcE +++;sq l $% ʏfXi$˷ 'GRs u{6] &:Gm3ޖ'R~o{|&וuY{stIUpshV\)c:: sGo(>7nj|h0yvҩ2ѥyYZ%$<z8ۢIWRorrqa BGgH,) t*.ݥtVz^{߱IKTQ%Z}.EfjvyWs nO8IJ! +|M6Ɔrbd!qa5y8b'/.qa鹨RE%Y&kD́ ;) r_V$˨('~j=ش`R{&R0>jo MS-''8&^Aɰuoq yybϊ+nq{sPթ)I3ge`rRd@C;EI.NȊeسaXm&"~hEb5 mip;e5f+$0BVU-\梓Fգp_sn/n zrI燝Q[ ![..Ķƥ97ns>N/LrX9K X43 5 I*8+yb_׬J l +pl)+\nbe !*O^oA++e3LhblhnX*sc7  Fb񓬱|Cg? ]TP +#lY6dNjO*H M̸oDG)vA!,!GŰ3Uh~G~1tI##.oWHc4p F"ͱ'.\9}iUoix'|6vt4EjC3Ŗ#xwg cłXls'/3l>U;VpeSؽVx.,,>iԘil0&e{]4ֲEU'Ez:w0a$}QQM{81;p=N?&O68s9s.8zsRf-PXe \:OBoDjV%)uDMŒ!wQ$])u `1x1#qd[:"9ig{b7ӧW3y$a'~Xc6edɅor89JI΅rn%rsy >V~CG=[x6`-L0EndVg1RfDXzWg>ftj,TEaN)4xrI7ka?HX+LKRȷ=$cl?``3 j!M0E֨bU?AyNb/εRE>2zWUp6lٱe䳈_ .Dp( +6N;;}F ln'FUi +g=r5'c`'6j?oGfFB8C` `7v)٦uH5#]&2=?t1hDK7Lm&g8+I= C6E83 ӟ%f O0Mju:rq!W嬕 zq7QZ%4c\#ayĒL.2#djT8mQohpVSSH h084?)6ȳI`txW&8 +rl<vC8loRɶ$]^cT^'⊣ CFiLFӈ{|:P^|)j%~ٖAЈt ȵ I`Z hL_Bc1;gD,+@mSoHgdŵFОBBr*a;>J8QZ0rKp.B;K`4.0v(t9򀽺ʄαI. (__\{_1 hM!$N\Xb6 D8O; }GW0I+ˮ+r':2B^U|'Η?pf`S~L+\qtЙ=bmiZ&sc3BI xc.ajY ^qe/F Ã8 ?̌:z5C}&gaZLwi4Uq#FhEz")2oTd LFjA%PqGL8p1#MWTK- Cq ŎOo>etuAVyG?LWhB:_m ӯ[H@ʻLs;r_|YWq +oȺZnk NmNu4avaM鷅ޭ2Aps<codq,vmΏg>} QSp*A-E15@\z9Aj :䛮u׍Z]/>$ Ɗ|&̣g)z.w?qÒ s0z#쒒m% 1_kG.mnS%d9FlG)Ee3WW]↌&ds%jũZ\gE +ƃ@^j6ifL.6YVo5 0}R@sJHn*t TiAF)DjN{߹DMZ!f`3%yO&&X6êFg`M#rf2 7D:ќ Ţu#qb/nsvɽOQTmťsq,.J>YQo(w֊h.])|G窍:NQo-l&1)aJlXuc#'+xL`[*jv|;5l;` + ʧ8HsJ6TI[c &Ϫ(pj 5 s1.8p8b_8HG]|SHA<ĨT )=k7(S#kOfY*pŹUJ6ܵث `r(GO:]jV*\p܆q޷\?S*ڭ89Cn¿Oz8*/0Vf+>b< 09Od[+v-IgГ]E:E+GF +[rF&OfƷ +ڲ0bemB%颲\8c{ln\ 65 + p tpbW@~߷`ЌP 6ؑޖVw [v,n=C0W7 >b =8(d$g&@jF$elYMJ8}R\[CeKT<5ImeAm ;._^USL k9n-|[aɯ_}ՓFcɷwYHN=qy?V&3xLj2zø{_.~tZ۱Rne(W|x&4Z}e԰MiU*!)ӵ|1RpsQj +ѧM)H*+UTPB9ut$D ŶL ۩էb9 αlƙ8DLjAk3йy1T A]÷?XI-FhEdԙråͣf'vC}r`Ala4fB+fJ`Vi B%ẘGA02gaQk0eX/MI8{mfg_;O傔:wwaL̡}=L0(SI<+Vi՗oVB^[uôV~Qgψ +8or?JԢ 3c&*\D-^T7N̍j+5aKv0RRE#Qc*>k يM*6ܖU + +|?%U622x2#MGeye8rq3, ]i0iXJnqN`vWe~֪OVHU *n(];IGǞhG?\B $Pc2dd7h$s/0uf%?eXbNzg|qu#$|wj_{"Mῒ~0] $N5 Dm4sq~{lI3[6q#LGΓcå7__//L6h&fe[1W/LYb E&̧3 :%Rtn}q~)hO}E&zysjۙ-9LDa- v@;]҇ +/?v:qq 7gx?Y[1Sb*}a!}|?bYߦsa ~{D߁1 z{7@u#7X2f?<}o! P h/M{*hoYh찰ݥJETfm~[d/T3Xx1o<3d][5ή0||o1<]83y:9_jEZ%\4&0*}3Zʧ^Uɋg->_SΞS0Jǝ}ϊ-jan+YDBS KZR!^szNC>i/J7alcp;ztr!=sہ=lhAW0t)>bi- tc[f#aanNSFb&7@'‘"?\ +\V8sn>w4  (4Q gUN_3]VWއ3NJe @݄t6A$VtqWt*q̥B?0}@Ma?!-^U?B+?"LH]UC"1EP(ye9]gj&NEw^M<,Bo`(m˲Ja }@ax(T򉣚;Nv)v+%0^ApϮl/T^h6X4>~b2Yq'Ϝh&w%9t-6oU,[C]\R^[c.L]0co,%qLvio XBq\0`y(OfoZ?** a?>mC +16T-o"_?kmjhu˹ٽ|d)VNJu*-3`3]^h6(>#t7s-{6?ysgϑa4rzf9b0 +%&gxP{ށNuQ-8Uk yet>8l`٥].z*V[t&~FV2xp6ᵱܡg` -}eH;oL/fQ2 oU sFor2R,„4q~NX(pr;U5*mw Y˷gcLtEHg.-< pxs +H}O)9g) t[N!|.(j3.[M$Q*ɶH ]VmPJ~ !m6#޼n;Ͳod} va9蒢L %Z\}E"ItHFA %ҵsJvG-GGL FjiF9RmO;3;dHd Wө1=;+@o[ 0mϝ=& ojRe|~ 6Ɏ3*̓ہ["͞O,l + fVi_v`Y_ gnɴl>.T rS +wW]!3sԊ䍓_#1܆TO\V.,:_fp0uGt,fƊ=8t ~7ߵOm.g +r|= #E<J0 fZ=D(&w yOw={]h8$'s?~vzu !~htVS)4e|g:P)h~ [,[Y)ZX ;MB ?<~hljCQ_8q|a28P}ғǯzfb]4^/h pgVxj#(F[2W) J +|(Rt؎VG̘sƒ&I."/L0a|*u~}Yl՘ + FEظs4|Yr,o$v2F-CkAVǡP(dsf=bqnEd pO*^V9ߢ =hIazԠ8ݷ}OޡZ52Fw'-|҄f;L@q޹C3p]=twK.E0 ET҅ȮuyJn({tP/C%nuaEZxs#G%l8rl[DBX\L.\y5$c|Ȣ記SWa0:. PlᜮLYk :P.X&7ޤn3~x./i"fmQIKWH;`Ph(1;>0 e܃;wnքQ⪄cͥlnTgj{<|kh"hFvhB51Дd2}{+oS[omٵILD2Xo^ a Pz,Y +۱ rG ؐd\4g-b_P@G$'.fJ J>Ҵ Ƣ"<]^Q8z[|+Msycq|{?wb7n' ;Z[bce9l-TDbBxjQipJFc~  !UhL3_ShXBh 6oʉ}> (dxBB`ƣ)l2EJU'd4wIOT8j> (ob.-}+@HȈ~Zfk^tKIKD5{X9 +)4(>@d$$a~NO)4`-CTaYJ +?P:*Mޱ+GU=ehR4АUB@;|8&D@cZe= ߪ +wo{F p@ˡ*JSj亅*tY- 6$TY[O.Km 5֛ ߨ5V~Yf/x[9|QMӒK`R {m/L_4=6A_LlWua4/8 8\ȔCROuMp3*SpVbm5>[#W?L:,(zܑ-2熔H^Ar߭]_0lÍg{OL0ܮtzt_;Eby}oAn\X@!HtFgF0u y|i{Z=֘w 4}!#qIPP 7#{D('P*>8W_I]J#-N{ºWlud޾(LΊ?EwYg~YkWUm8۾x ϼ#np ^kQCG S*~ o#0})ȩPi9f:\ ~e~5 +я8Rgq8j҅ya ,j]:,Fq:)ILږdV 4/WٝmB`+O2܁"VhN*GYdu6Շ6`FZ ˺^,,9jErViMv|t+T[-Ofy +sf >W`2q:6.gf+2pF CѴc4MWa5ʆOi ĖL!܎<oMP<v^~˻MOT{m3or\^څH槼/nk/m(떳J,USy +i;T.PBf~G+7>&-+Wo4|DFOOQ}ۋhmѲ ,:cҖ&IU\G7hE,ۿ8h[XBp\T \u%KD"<}m֓łj23&Mu@8DHگ2ުyRlu)ݓmY[|ZrJ*QexnAZ#hǟ ~֤(ÝMz*h}7IHL;Jn')tmI[XjƞLdxu_\b;v=O~ "b>6y-rɥ=acx9Z3-Dױg'kjNtm&tm!Ǽ_G0ˎ]o[C, U1K  5s7rAşMj4&er(׿%yl"ډF'p<<\}i޴Kة^Z9hP 6 2'M每*ΕkSUJG!ւӜz4ڃB%C4,,TMzGM +\4Z#hϤEh6U8/M Á߄o ۅ!{eh+s.n~)f1"WFƟ\3%ic=\a?΢:Zi!Cハ!ۂ¨(j)'ɞ?r{ Dl +ǻX49 +:9B FzBⱅN[4ZHYT4# eɢhRNh59#bigFQ)t T&N'SMq@\v> ՛—Q Z uc䵏[ㅰ zBp{)hڎ\di_?/ -ot8re9\m++4\V*7\Lw]Mytm`#~ +ykQPC Tē*~C/Zot +qc++ Sj縦`zuۼv"_u8mŽWm<@]Y[f;Vp֙_⎃fie6Ӫ6l+α|[|A'yrMz4,}ѝ/ͽ?x9ZQ}[@O~)93.Iv[6 ΦZxZ9Ѐ>?ډ|ױ(6D>ż. fY#pnPe=,Ѧ/[q'd}F*fA6:øʾ;e۳v+{ru~Uay $Qxߵ'9~ldK!$5KR9<|TCӣy[L!C˸(d&,DpT4q u Yم4<%XFХE~pa[Qj\ty()+).m>KȄek&;I%vd֚{l^ܽ ;J8*jQ +$jDA`-W_1g΢\aYJ1GHZ+"'/ P(7ZKMDحP;x-Ǘvv.(A/iE\AP:_SBز I;6>갛 NREʥ휟/@nv +$rA|)aD9З_w>^. B ,COϸ^7ɵrzn2]nwVY7qjaA3?h[uV)b`ODlɰie'rtF."(&X*2ڥ Z-WHђF%DF x@-,MHv] d|R,~SчVA-c*.e,cwً3KG_m?X`5َ_F&FwJ C=nìi@@9!,rp53R4|F:!A(*ʌ М}ްeʙR#b)Y@?Q"V +Wy ez .1 OYR,kI $VTN~C¾zT9(گp!+*w22!>XSc 2i3!wj]e{~>}\\OXqO2WeCkP&}h.4y1(3kҋ?_UP*ܳbjW۶|OlAA&uҁKV1xhywbmLN$w1E2/`'&d4Lh!i + V)Ib.ձ17|h䁯 ? L^K37 UThȐ( ,bgZIKnph_UԘ[OÖCzLG"V_x' /t+9Xu1LQVR(j. 8oq7+_侠 `q}I$/5iFGd)sG3JHa:̰nƇ0ەXX깨ԏDB?)BwŽÎ*͚-,C*5N4 kU儥Ġš>\$ca >-dZ^tۭaNB'޴,<ΒѳB7Fq1ak1))9*2V-a.(BC*uVLI} ΓJ[*cY.B~_k~hQ3  0b$"1G<5_r(D4~&9W`?(VۓرA#wX#1UP$ae%~<_Ù:{3]f:$kUXrt*o4`5vD/#Guo]O$+|n14lT3AL,'Bm~$s'[:YM c ƱD([~XM_{Zv;,].MnI J8ބ#~)WAy'ge`87l[`e4Jd7BdDpw}\hpv/3̧h}(_lcth/8[<-Hk(kOH#&_Eo$A鰢, =Ġ{8Kw9{$΁ey# 3>wY]s'Wj"9/!(7O]OX y^hdd@0} ӝEE&Ԏ濝 +d\Тe ͟`cނ=P`+ @,b  7^é 1Y4sjCU{{[a%a![ۅ_>7 ^u:W\)b ٻ$<.D;2RhI Z7l&`>iǬв%sEvOE[♂T}HCfaL4>& cd1ILбJNirh]|,+JdE+C/ӖJ//j+Hq)ۋ]#N23KIo68SlKH-]Y@1D8W%2B'Z9'ZI3N hM騢R_nU5{ y(dyzƢ?a5;CZyv& Mҩ4t[I&@.5ɞI=$QH2wG#7 q^0d ZlcbbBB֧+IC+ct}"4ؾFh[co + ѤR^!(5gM;Xh1`KgKc?{8ix@fd S6*j_zյ$h4-!!/6KNi +J.Ci@(;Ɛ/={,[:j;B<ݱgWߣ:ܯ/Q{9]cimV"[=:<% GC {G8Y:ߚ0& NE3U5t+T^)4ހn#` +5L[-,3udȨyVVԚu ONu9+$\ٰfFn,$^d]ч\yE\%fR0#k,HfjͩĬ,6Nb&Xl̀$ Q76y1"x0ȫ!{,(,j*o󖫩  We(zl-7-F)-Ajh }ᚡ0trC=4/H=xy@K1x<==UI6b+[?{65`8zJӽ~ !4&d QJj *s귗E1ǃYʑ3O%Q.=Z,u'kͫ 6aY' N[^`bNC8E&H4xOY-4SY=Yf(1 &n=I(D -N94+U;Hu3;PE] zni?{HEhNdM؈/+D69PsIRvڣiÔc9SӅonu{ʔWiY3Jjt8U#Ww +#.OMzfAvwt}E4ߙADwXծߺA[;BK0l/~EPp滁Hd9#U0噯.ȃHwd +RƧ|iiL0pN= F g.z%`46H(,bfQ6ꂧr ^P-]vΑg/̚A3jngA/? h$g +m L@#8WMn_"g۾38t"pD%h r@ ul>0f} ki+ +~萞,UM!JWl]KL&iH23VWQo5sM͂Sq/c*X1>1Mv v k: LfKJN8th?tHB mGl^k$/ +az ^qiY#|dwŽ0쇵ȴTi Ɓo,s'v(S٘|V" +rh~צ3Yι^ ^sK!MP;iw O'frn#e4i> TlӍ`F5.b3b(b\#}:t(*PdUw9] ^ok,%GQ(iVb~+#COmXq_ub6x X;YN%^yɠVNZeA6ɰŠ,SɥyK:{} +GMDqڗh_05Ájsge)N{RJ7ܷ*]OtcT9UjMUANm͹=c}&O7ȢF9T%4eJ&aȁrt?( +Xbig0c}ht4QS]k5|:Xf^%D@a$mCM8 4 3w {@a"Oݾ\aw۫ߵn@.أ̴,΂GxT[YdiYy SNiumYEjr9,{P |@Me>hߣ MسFixJ<ߦ''9GߌPh7*23Ny'k24l7$j`K]nԘk +Ӿ }Jiͫ]k:cHt'|(Nd +.B0o;S*ԓ9F4Tf>uN%&WHJ$>.x9<)vmkIQ6B%,-+IT)++?EʶE툋HM8%KqG%gKaksEYrfun$i^4, &58%[V׃K¸JG$"wvEq II){&~L)R? >;Uw9ql_fPiM Bb2J̍ʴi3ɞ!Ervڂ Z +_iiQRʌ%EŇ#oxhy^-44}m*0voq0M `+@})ErwW E^/2dszeruOd -96QbEyRxL5Ozuy^^i WI惶G0FŏX4d0b6n!F#bDh~d$|P^wz1 HX:J][dRae40DH!sFR@p+~o b=Ub4WTI gF#hL6 c:spB7h\XJ&F+Fr.媍8#| -r-g)#U +Yo6_7`vwr:ឩ^eҨ?rVbrݱ@3y +.zF,~t=\'f;U;t'?nh4e&͝zQť٘%(ޙ\MWŃ/]k$:(5@P.gQe%d.{gcP/tt9 +ہ~ 0d2 q$Y ]P\|}tV4%Shsſi`Lv+9orEe~^Ȣ'S![F"jDqG=ɹWz~+h2"zwfUUyBõbD?rAiVV/m߃9EE"h/j֩f-!wYevM5Bo(lJ( +ZtDb]H{oD[֩B8@_oٶ + iRPCMDL &NvmoH)}$ITBK;R+ ԗ +,kFm]:C8J4xZh$\dUR;cU?5 HbF;X +[&wi, N@?o)`#HZ1 09\B +s[^XR떗DOje{~P!3)e%Ԗdj=aP!䓿6w>>>iI:L ,M(S)xЈ#G! sHS4wL,eqʼnS  5zujg>p7)o'Za+Ϡ'OJA佝$=8%ǞTiAT1g΁X*CJӶGeF;8бos| G" G2q:G3OANW}eqE˻.k6k8pyW8Py=G=B%͈ YJ0Mz%?Hؙh/ )L@֦&d4vU;=U# *),ο3}'HH1LH5eY>Σ׬3ȸ׮\A~pfV|IF6[?efOuc>-9wt=}UW2'S m֊|;`^`b_ccNI7b_HSI1 "F亐zTM@Ebe2wvC9Ѳ,%%ď>尃=s 30-#lL.9/Xt6Y,V  #ADŽ E:h$^hUt\<ƌڬF#̮;[FpFg`Md,WQjZG=HA??|!y[C#ĘJwoAc~F]voYY#*LA!oh~=k3m Wͼ0 oNƢnCC5U0 G`tVBA{@ YkMdSӍUM癮d'$9eq|u6;x۪jo[Ӝ;!t/̳YY\{BI6|~,3XYy0?-mK06/I'a&_ir2mōFj~IYZE1c=X m/3t:T](l0#! +u&Cl=K&M[D8:9CJte#UҥJB^c,)*8+1`O_=Y$0c0Ts( %N{# z5.,1d8"wO\yb$K04fSx.?(elD0,^t1PruVD;я=&oa\NX!*c@+ۗ"=F$e&c +"2Dʵ!9ur)s\8|r]2i3/F1GeiJtptb!/3Jt PX;mvEKXGr&)ɜa?"A]3zj[`O@ 2$_ѧa)%АhxR-ПNDžS`^Hw#0 s3)d>ԩx F b[$L [ +LoUUsL@bAK;-[ 78cD̐OD}kj%m- ZFM(_9\S2"٘sWeyJ{ՂUӱ6`uJdɋȘ1fТlz2Pդz(ä,k + I觔4(Wc4JķKԇDsP( 6DLLIૄwd*Fܱtl ۂwHCc6RIjmi9=|BwqZ"&W s%@Hd;_.jru4͘r.t|x]$WY[XHC0XMEzސ +2-s-j*˧u tm +u;ɸ;p>X4F8 Ad$n1)=sKe+QkʯC[jjh~L` +]ŵ.$뱎e*RHRY30}5Die x8RSU۸ !c-~DKeF2+~21sۻ7_WVˠFm]wAs;Ds|%,IL62\jI0\%{G/-rR}H%(Gx mW7 Pm9{aRzڮB$e3^e3EZ"1+XiG䀋C8<,h]ƛ[ߴAJC' r\hqWN765n+ut> [5\}ֹ [j5'2KMPjghq)?{2O'аg4DY"FH +D,y%†JyaGF?k\ >B\= gn-:YFh]+gb^2Ox&hZ?C΁}w~xB[F.fjkMՒDMzfB#k84f,Py\mmQ%xV*Le+ +5m j6ƚJ;ж7-kӽ+qkx +͙!w6{$담'$A>><1&))&N=ѪIR==o'jQ%WhDJ0{/L%4Z,UY^[v@Ѩz;;k0GƼ&QhDc>[ ]椯,Ex_{i%i%4,kR=ۈydڹڳ0Ah˥Ae;Sw`)Y_C*K*0 H TvՇ7_ I5^21yVJp2Vvt{3GᾹn jv^0(,s}}2纫v+<Ԧ4.+ |Vo5ڪgU&dX],Eiɒ%f,Zy̡+6tz\=u6&rHQ$1'cSBy\qKϾinr ?{Υ.5ܻS*m){7cd>+:nm0vӸlb _βYp>7C\;Ob +sVw,0 +L3N(-ѝ,ԼK0c4lKhrAyۣ лR +q|?\ƁbΐZ vG%8s8 ė +eF+q<'M v6Vwf57Yoe#@븙4Zi+O0NVaĐWΏAɵ +#/gT(| >DX K9尓F5pZCw)}aV|ytfR# +bb칸D"iqЦ4u8m-4,irv ez*CXɬsϾ;=ʢlZ5gNT蒵5V(D՟ +2ch>VFT!jvcyNs): nu6WWg%r/JgEEą=qJ>`,*1L 9ŤF8nKcu.S⿛dŞߤ% Wh8cfQ0C0abIp,Jӧ YBpF #sa [ZT7 g7\>8((m]g}t)yҚBAVXy؍j'UߪF#az"g +֘˜,NSs],9Th\ 4BW[Uߐ#Szi p9X(O +kE:Z9K}7a F5]35110Za@Lwy+ڞPLwPOryU8C?aL$7N6lԆr0=L>2\ -Fl'4d}&L#Ïz,AH,}B +?'')s&6',E n/Cʪ/d$Hpe8J, [} m)/nn3D_&25)SP*Mv i~-6hjȘ} U JU4x-gAJ[aWUI`"IF\p**N +KDGBF/]}$~ÓlL6S>Qo6:֧aju|Pj|т]f]*|z1s60A&"gBy-OJpkUrh,4]Pl+.k?4Z,ux_O֐Sry9c*Xށ+e܍mW ; iwҙwy[`C®ӚJ]H4 zM'6,>61)|=?b nVYbPG%ꔄXEu0hGj оvk)׼5!}GF|iJxѭWp[R..2S5[PiGrbs.G^9qu#7 +gvW.w@hn0BsS) +nC>%.P72|dx/{f #$>v?o  +QO6d+ef/ȊeV5vT$~[b +݊b0Yl`[+,Ҋ.y|!Q-.{DQ!qAաآhVzeߪ2+/ٚo} +ոa!;&SdPILqk (vK-M_Džw'd[?UTgJaX!g=yBq"5K0*xoa8$ra'hTiU\:{&fFQZy: {NtF +uƩR0ޭP.~+{}x !?mem? u rt JDڎ j.cWӥGx[!#O[,\K:y:)1"λAccQO}KK J'hCΪU7dy[m)ļ-i>k8.o3wqg`0bL|ޙδ?9袎9z^6'YT.ް7/waCHÌ]j\Սv?8m6 +>~M.]Yn+-S]P008ϗ%**:c2A-9'~a:nbGkD^ TQ>S'@Yjo:] 1zE)*Eft''EpѪ( /0X=8QH*$6Qk (E'}ly[Q"`{(/[Da:|dZ $} ڕ՗\,@}Ew߹EQYru)cE?gl)~\>)g,Ǡ ?XOXU'[k`64q 䅆ҭ!)+BA4խ|%H)K̯|mO=t}Rg1mGGp@}NxjE*K[m`xջ ATgA:E^zCھCO>2~Dח_!lh5|g~ *04Y[1WX%vJJgHbƈ' Yﺼ\IL 0F,^BG 'b&IM?; q.LcoLF"~'RƀeF9,y1 < B=CN-夹ʬhFrFsIN"Br,1=8MhWNOR &>7xUJ^Q ~(ZV.?u O *9Re@i=f JMN['L얔XA7w @CwN?.ITzmdK9,,i 0Ǔa&[2)mrم.lJijM?Cda8p$FbF_P ]U0.I3A3]|}tT1 &i|MCXNcd%b>}r8a_ 'jNK'tru 爩26.N>RSe,쒷GSgcc٨ͮ50wT2e\6Wh\{ն KU3a_vB֜<TtY^N;Jgh#1L^2.&!#FTh~բ7C!o9$9F_E8|Ȣc,&P57δ5UJlp 26ۼ*j}9Mh2Ks熇~7os;`V'q]A+1,I,&Y0%X+T 衦k d:2D8$0mxs/KwBigc= Iď蕥N}`x_>tOڲUiAV"COO2 -SNo_Uemяt8C$7xB(}K#?Zs>؀kHnI(4=U-mK+(q#azEqaq"Y&\S5i|ۂ}q;fmp'b4pD־4ȜM{5IOU(ҔR9.&N ZQ#'C25%d2.&)Xk(gg_9֍{k ͉ ` fð?0ێheRŘ=esişO Q%0|:g0 ʣv^W9KZ<[/!Q|LiI|)drc67"%mI68b<0_ //|Jʆ3͹L;ՁE\]{ f7ߗ(L:`1zޛxC&dS(8&= Mlxy`ҒucW{G&· L2᧛]ظ[E' z"(%Im;\։lN d΂qO-KOb.ۖOzk+bydo3?i.pK͋3ioahXL{&t=~]¥v:9̅)gh,M3I|y,$3r ?ƿHZM}Mldtî+7KXx@$dݝDf},q~V5,:TSEu +5t'U\cN!q'c1 +,H܉8T_.L,^p~{8ޛt9{Gms+|쩬*8醙O_u!9]J_=cjB1^P>Z5?claUX kVn%ir0QQ9h2I$uYJ;T'Qsl7WTΌ́ \xJw6QidPlb?AQޮ`_;u=ZQRCYԳ i*%Y]nes,&XooQ?"X]ԤcwD36 F+R,@. M\i$ZBx)]zgo%01+?zd-,,Qj zƝ,Mvd&N=>ȳ7VWc R>&P|\U|,mte%.FMwW|RNIWͻy{\e^4sꏖL6E-S#e +hV bfְ-`0m57Q`913 :]85,-L,W>WzXnGŴNeo;Mi"w +Dw'<4PӰ܂yz)"OO򨛎6J7^x0jHi9;nM0C?#^oXբհ|6B2/&cO /Ŕ +ˠrELU&.o:.&ي,37;|9\#a*^UTcZOϯ*9%͐%?GO!Tkjr3סUSd NIVT̼v(3Y|>zaAPnK;LL9ldtޫk.[a8`J=;'G'Ys\t7WRÏNtͩ@ 8Ä ߼8SCҢr@{%[Ȥ*v7̂0Z} +Vϲ6q_M;\?_<̂1\x|W61iT`lIƪt^8(^;vz'_2Pv#$7J*꫄(L%QIk'j6諵*%cY7L *oI\٦lDL2H[CtWpV +Ej4-"dcYJ=$f48O8](|mku|QKBȈK2STX΢Zx2{lu duw1%Qّ8t Cufyi Q4'l !}_ e.W"q?$6O~IxҼ8"C|Wʣ/o~4`0U0GߘOi,>}^nj8P6(ghyOJ{טؐXQ4h*`8o%%`ODMp7kwJ"'=gp:{iy6heL!r%`m0|3rG-'gp`D=APߗugJuYAnIZC?>+TodO彇V/fC'<}7/^1`cWSXr9(=IV(?vz˩SY:ʿ09iG6=:<308v0jauS./ s +GFD;ocehQRCz Xs:a|R"K~I,0'prK)D&ٿ%C/r,e Nzv3w;4#=K +D5 p<,` p&Xa<ՅY3q>bÖuJaH=%vq[E֩H#߰.kȟV-&o70v=jJ/\C06g7IzD=ꄹ;צEx]^M5.>^/ z^0sRu8{҂xe:Qښ>fI *p>J$j[3|00 :yCK]>E UڪKs oX>a]XTXSƈS1}$<:7>SːAua؟m_k +}i܎S`N7|;@۳gMG{G]Pr!4(rEHjZm5}b)| 2C'rU F0}☬Tz5nB6 +ǭf'cF-c?@o{gd8e#bk\O['paDЂs|\u\Kv!D$ fpzPo%T"=\0'' 0c78# V(K JҒ՜8'_Ed#l7T K[:0ɤQ8`Q*qv| Ra{'Tc%Yn% +#wtT0?ҢSʌL1קaOW\wT!_HL 6`y’ٿ7IUf(=L `Vk[HZ-j|W@,πL47']&tFс }:t,[f': 4..&6Q84OȎ~VZl ?~(M&_T\x.J(H,׆^/#~/gD}aUY=. bíBKM'8CRR&P1%QgK5Apď`n N\ +kFxŰ%BW0;t/n\K`.ӎ/[|'9ꢹk׿+E:b#2ƟƜKLF*qVV|nD32ȍ00߀;`jljv85tT9=m >f#bo:# 9"3hw*njGx643-L bьvҶDϓ W3tb5n>n%UTo5KZER@\S5UyB.17U;–KNcne$e;u}`Ǿ.'BN< B}^QpQ00=?dwZuNHQ%(q@cS2V eW>k?0͐%2ؓ,=U8TY\zBj|201 Emx:p;wvgn`#b6_Nnk rr5E-}{C=mYR(fwr>^p7 .[Vynfu4H!k*P-yk@geӞ']e?v䚛nU=f` gaU,xxDּ̎;ngAw`4tQ NU)RU^Y\UW3I`ZfoV8CJKUN.T| P.~t}**H[$+T ZԣOڕLOHPJiwOt3}Sa,1t)t^ͣ+U8Ur1;sX#4Oq46~}a3%!ݹAܩsG"qi:L!61 #j:gztz\"†+fY* U X$/V}! o0sGv;:7uHjK$KF[I̊2R9?GP*lDoz Hιl=9/0 ߦĒ,B_()q M~GJmRMm?ħeu^|CE4n@Q$kZE8a 0DkG[; ZqsZت9j҃Jih%h" d<ᣇq:w* .̼k6'p'jW01{T 3 侷Ƀp z|-glmr)dXN0$n"2NI * P6~MPtjS=+;xii/u28Ev> Qgs\ZڴpAka~LM:2X [vKV+5̾B$[e_Yܿ٪݌[&TGq/^;͘tqP}G۳{% +z06,_ʭFn:zR>F:{21 [@$dtU7a8x$\Ca:Uz;7 +n<_FN>i*Zh"a'G.(ʓ0>$tasze`z҃4e&#n͢&Sc393qJHD0NrI p>i5?1#ěB%]|jM6g!B:GkZg?g9_eφG|}H:5J4f/kJTbd񄕢p*tnتȸD&!éM"FɓE8G}5fOZ +=LlJ] :{v~Rxh [k5@n%y&)Z0|sT0u̥G?%+tjY"8)`F?s(pPԪ=:|<5_!IZ٬zUjK`fXQȭ2.Xֆk:%Vb7M5]a-}lq_6c.ĕ,qWv| la "31ѧB ( (W*?dGn&]\r]a}Ɉ/[{is&Q*a,sֲK_:R}]gy~ E@I$(cTB6aɭJYI"͒FgN\ww-&kML6;iީEv'尖 +njMu8M{p&݄#:3MEbqܵ{#Nzb"S|6AKuk3୆)tAx{H,G*+vBBT1xm- *a9@-97pQT-,xp +?i #l.!Fko |ŵfXKfpcBׄ21i蒢l64>P._U$]/[pNo]m{IMi^ΆŞ~A(>!#Vt=y~wJNUNL_DJS*))~Aډ]bY Z%/< 0p.\qPB*_>Ldd΂G 22R)SmjeŲlpRhҕEyӊөT*TJz advv%9-~X~\tv,fʻ:.o|Ӹ5aqeRJ;aO/وj]][CoxO*턲Cbv|UjkkN)2ɠZh/x $z%c((Y¿J3t 1U&G6FXgtgA0Msөvv. Rƣz>u3=8nSA&@[JbjAUxtL1iw[4:4jq:[Q܄Vhi5M3yO5t`Ρ 6$l:Dp"ZՁVa#X!ju%[r`fz8k8o9Ep<5{ojuͮןc`0p&H*!"/ET-xM-Vц\M~y5kq*K m-.8C@ o⌷=`5Jm̆חFʿp9$Z]O}a9=gCoTOz{o|%7sF HB^CR;1T~vlq{, &2RVmkv}mLLIdL%Bk)XWZs9:)lm_⶯Nm\c;ov\ҍT1ZŎ؅ȥӤ:ykoKK76.\8~%t٦/^b;yeC벩 jdz-s4i4řg$kE8-A upŎ՛\Ѻ*c`FvCila7[2s*m8ړfv/{EQ\mHM5/ i qAp^;EٍnS8wvݟ >PR'p7v~ u 瑧;#jGޘ#9vK;?002ahS|9$SMV]-0ŢVm0fe3I6`a,v\'K +FFQ)&f +ܴcTdf/j"^jGB^sf?Rn}(gyP 2 PaqqAAӠ}(}!0k"3oU: +K{kZ ?:G< ) }D:BJ M)jP>)PNUrݪޝ-qwΦyiyiy T{xǺP㚧_m]}jk6xmahz^Z~Z_E>!3'Aj#5DR.k-781cnJjXzmz<- +}$3 nEtQNl| "׾H\5=-҂#(*4R"C' K"5-L^oӚ ;A)y"?6Ev|Q,: ǒRU4;#Gf?:QOw4]7w؆KK|]_4WEv'q +c ӴN$au~3R2ޢo>u8>",⧚S:njp $:UryhOg+Yӕ B%|%Y37}Q/3i-H~i+s+Ai[ݣ{3Cyα)/[bj|oGzqWI*NW<<!'ac[e)$ >qk^Z*CV%{9)ٖ)Mpn)scz8ieIf_6][vn701qI ғRӥЯyܸqak~w`/HWͽȅ9s;?}>{t%6/^5B~hF ٩YsT3ް }Mzs6<`7@z%?Ŋӹ%T k8l~ t c6@e$[ZANIje<O^B*,*dpK(SdVQe0ZÙpZ,4jk{ib1]Z3VB1e|CE\-/uo|ͷCtJg E'##+X[t#%_lgg"2 +}M=-,_O5#b6dn!>+FJ I f+Ugm=d|Pλ!lqrcqmx0L)2ȇ,J@  +%-.5]x4aU(8]`8R,vVJlgBgV;922ϏpFYJBmL,w"{('!8qÉ58M_QdRkEB +btXJ R&ߺcw:՛jA`uذ4O;#8i|Ae'c+H?"dmmbpiƼTcK1Q٥Y +q )urBùV$)ha5bMQ>$g%k⋭Rhe 5 \B7^Aj2F uKӮkF;';uNK ?TືH}B +γ,?ZD>*s$$13{;tP@T.aHg*E,RT5cL1kxkt(#K԰Q cdG:*C .4oVHӉ#] lY|g +F(B1 |3~OrUYzYf +R!CEEKbcC☸q pR_gn(p z8|ﱄHJ 9ՙGҏ)eɄ ODZg^|͕nhǍV +tYBKGLJp&۽Iy!C{I1u-XvM)~S&R{VA'W&2:(T$$T& OԀ6./C#DE{=6c?yKkeSLV0AC}-V"#>ZqZ}VƼng!"]LBQ$ ketQhIih-HPȓӘρ 7Ҏ#>}?eper1ƹ5 P3zDcST`yP `,=V޳k'2whc8u#_ ΓQ\wx=j lY`LH|8͝9Ey%t(c|]LMT`b'h&St0 +F\ጮn(]"=vH~Ҏ?<2D՗+nRJh]X^.~4XK.7r_?$$;[_Rx+ jLȥVaXpРyܾ+amn7_WC f- 1 !GltmLaDFxZx@L9i~zBK|9!w9>o..Jad'*jY@.*&@S`hqB$KMMC.1R_rI@0 ˸w0h$` ٔ p}ˍ!oiyC}urn.MYμ2>2՛`}_ƿ6e ̓K3s̉$րpM㹦ko΋If1ɱIkz.2(:AswTߞ+.CVql԰Z\HT9)k͎Aˠ?cmq51nVs^=~ψ'$ȴPM9h0yu[WzdnJv|A4-R#3!ƸGDE1q{L\3>Lkhkp .@ #.nxIb5vSJ˶O&XAMV5\hdfާdfr*yp8_&egQ)=9AjF[*BDW?(8gPdGȘKt6Nk-V`tTϸ{fUR*7t#.!З$9R3#Ojlisa [g@S a7almH~ȩ; /CYk)w_w><58G{ ⥁Dko)qhP.hkřU.OcFE|ǧf.z`d ~bؼ 8y0{\u.ք}>㢪ay\MA?Rݍ̺g +FUĸKX=4Npщg\Nsh ~1>-H?m|]ceJ?В i Ci +R0l+[ded݇)3י~2i`aV09=E*h :?p PB?ڃ +R Ao.' @ ugV)o>"Jh$$葶p"w;%olƸ43mШM׉;895X+mߺ#;դq|HEUv)HGhli>b\3=>owiB|WJfSS.o kh,|@/d߹ }\ё:l);\2l2e08]mtkN>MFp;lwؑ^ser0::0`*?}$ r*/ST3int@{ţM\uцL^Ll6$abJ.9¯,#[\`֐]~TGlrXn'`s +NYzEܶDhs9$J|E +)B2;|Dw<}D!b f+pn3<7Rt-~> \y2(a՚ژ{nepəҊEL:M˩uZ9BUg6,owX51 7P`CqV"1*q ?#v* +S7pX.T] Iܭ|5H{_Z5.u>a= +7ƣmŅhT.LX=y\VFg_\zipxE`=Y3 BxLB +" Q"Z eHS\է|m0Lmim½G QT(aéSv`4 +Z !a +͖?@AV`,3{D#fw o:c`JRV+*~ +?ܖ<+*LW3|1sXG)QnVeDB1ԓ`,'W'Ьף77 r\՗HD("aowζ.'%[,isg-"-G(i(=SSU%4 +,쬜UJrDv~_AщGܦˉ{ XaWi, 4ISqw8ԅ|aa~ nJSRRD00,_0]SE9D m?n;v1(vVi IXYlkhb`3NiSna9SJhmG +&kҼRj R՛aa%ad!#ak'?"4-Nڊ.sZ9~D+c7L7ΔTNaغ4:n%ۘ7zlnvy 0O'IKڈr,ud|m[ jq ЏeNPs3{ p0B0' 9;s꿦QlqQJPׄ#`A0OKGz(Ө4P㞳િ CIG;&_r#3HV@R4B󔜯"Ȳ~YP3pv0MsK6_ۅ`}p5ڇ\p"jj_ѹ%W56V?38!B7[7`f0 FpDCm`߯MvOY8׮&ͯ#9N)GҪyʚm;1OUWdf6%J:x1s*kB +qs zk9w@" $e&D䗚,f-ڣ4.}0[;_;ci#mVNaXu{r[O~|NET%;-|c0ϧCkIQYSgҠ񙑷޽ `\HHAn@XIHɃu5a8twĐhoS(ҞA #f|7߶1Uo5|Wn ?̑|MbcH rVah:sKpf:W!.?Y(fFuX` y(p,UƵ{p6`0zcXOPZRp22HFBQ3/2ihP'fG)pGQYGTGVa̰mH=)$i׈2 |EmiiqG+##O_7C泗o-yҔuHVg) + &#eV'$x(jXvBT'h{T:QܚG` v q(yT a"EJMQC~/W>LUhAupyUx`txTl!&.#?IfIn>)W&OV(=\xl]+$1d^G{HD]T>,xƨ_6W<܎fCB>1j5QY`ND/'\GtX56B#='BEdS~=jI6!`EynnO:\n> c@4И Y)| +WE$'2?ɔ:W+є_jy7?ݻ5 eRL.g؏Tw[g3?a+QjuGM~2JRrb +F ?_W.inbܝ[7}<'e= +p1Vfs$(BL}50T x h~ɗ叄,<>ǹ j|?f4v""A/-p I jH6a/}Us:w&C#H13UAHiV&I J +`^ʕ&8,)O`> $Ǚ +[q\#3ŇdIږgʏĩdʡev[leЅjJTh{aa%G^t/HxG]o3萘)HUR<)4 &ZOp62jYšAHY)`FP7w<Ѯ ڡ_|.>&:ͮ׺B3wg[㗓f^r5nWL dͮO tf[m֣8>u/(\;o+UƬ4 +Xw007r4CjV N1/dOW +4W>]NRA FQ>&'ptA)Gg?iм%*EYYJ:}:;騻,B} 8X93TILf sJ1I'_wRY#0o'ُu:Na?GJ?Gsp6ٓY:d`m}<Yi 2P k)mk{3EeEs% -]r%]L5r6$ΐga'eTsٹʆJkěj~Ɩ(eLYbJegO e4:/>Ӽ#^ª»Ч:lQӹuͥW? ~$Q+xHva% N?f&ͰމLӲcf{쭏;JK+^Ehww0~SD=O{DPoJ_H΁3\1丌ctAv$E7ӑ4v^FgɱJ] -IٖSiw Y؏;*dq\ư#T$xZxX'hG 23UfG} %988 +cߩ3Z+E+_;^T*LL7FLJ8\`H>ʱ"ְAȽm6t|M$=0Wu_-xÍkX{'aJqa)G+ȽZGp:. >,äp>"[9QоSXLA0@.)ʹjCrh>ȂKX3ޮqSxD6Ak: H:d UV͠?$.K(H!$|2ٜ7qY%PlySaӧ]ma4[T g-t .F?_&2*PJ5ҕG)f8R,7 [r{a/4jWWg&̂: lV0 Еư8(3 /o;)ia:7n`_Iܗa)$h/^ Sbl+Wއoq42" ES}Tl9~ +N3E},mcڧ{s{{8K$ hthgrKwx@`TnRiB' +3D>|HG<+E7gbՂ +"<`i=vm?Sޛީq:ʫor}qyIrya8p%JNP}K}[sCӡP_\QG]XeǺNB;PB~ܦEá pts87q!/Г< qn*>u'wчUnXrw-<_$#ڕbWF׮藯|RycOþ,KMȰq ЭW+3E#ڶJjž +S&o <&^S)~XY%Gdلˆ谨X!PWzє{ Oj3'L(ͽ#;g Fۏ4zm?5'K 44pߕBfvjsצd-rl'cS~8=ƅN!sA'R*Q|2D Tw3S{/\V.c MRcyVTQ! Pm<5dI*?)fGè MsUślq)>!LVGEphFT;TfC)y5f( A{Kgƍw b-}~a)-l?TH\Dsŋ ؾ٤T&&*Dʿ_~—{&K'uIJ $\vh@XĖ>nՈ^?yqv=p|tp/t=:# C _rB6_ iAoPJӢe~ĝ8Pp +s =D܉a>q{M}1)f޳D`xmٺP(1QI]ry<1=[G39`Aͼjޑo-wsrsreIR9cAd| xynzOSv$.™DŽ ,!ndۉ? #GUsGkHWuo%?t&$S ̡f݄9 +@#VY1mah'!j'S0ϵk`'XAFE`gn* +%[F,*TU&KQEFZR+moW-Q Fď8XgN#ځ^? ILE`K(` a#EidLl"C*ͶX7_&`Q*{(qa/͹R_{R?B_uJZ.RθV҂- `-' ?s=L@.E܎lCE*WnE,rzRuo^J)1ps6*7Y*KT K ʘf%EܴaZҲKQ􌶨Ï05GU1L넙m06FXH @!~L\~OKQW{t7;v<;ΧP鲓} PW*eBx< +0J +T`(\^oܥv>on' +2R;E\I2obf;!pxn A_}97m$}`uܹ,jrkb*Ν14]"vsJ2lP?$m? ,?߼_hpyR5Lէ+_{![aP nC>t-+ }IZLKzx?@xtajCS˫;SK:N' $ ҍa] +3 F zЁoזDgDd\i4 Ch2>9 +f7p$y(6DƤa/tSU|F{ڴ2#LT.> 4t^L +i!{[OկOVQ0Ui˺z|srYۮ3lniYg>5wںzПak( t!-4 +gަO==>D"`Ǝ&I&0~ͥK+lmcO2Y?,ur;Wںy_/&b姛ki +~Wo ^$u6SҲ8L0]P22U7tT䖑>xqyKY$"lg_x\9հ I(/y tHAyGzua*Y0U ܡvkqt3ZG:tq8ڍQcmùSmI4zc4+2/ wߩio0Ywlj>0Cx4] +^׹;ꬥM00Hqq5޶b`b<}_20K]t"GExnث?WC$'w΢NVՖUJ8o] SZL#b|]u;46=_N|ĕh$'_wCad"=Lp#ud\0{n'ߧa[ߖ&RѾ{Atc&23ag7/tMk(Z8ě8 + +MV;WślpmYʏ.)|IVOU*V@Tm99Mh{O&d׺vGkp>% +##F,=wب@*#7[Ͼj^&*6(&D=OCߢY>i>2n`?2я۴N0b]\y+6A!BTe:LoS +<v 5&2N7trOP܁`pf&<%&wCHf0LHH:#`3CfHnl!;Lz{^352]D_' <}݉O!=n`#Ho$ZhNu3G:²#ABhs;ꪲ]՘Vͽ!qѝ䇲J`9E8o +'W=O'C&C9кp=ށ4C_nwEKF՚. ^B(bR}D\844hfwf~Z!=.T`^LڄWٿZ shOdpԅ%%GZ>qT~9-0O63p+!_JYgM`^m'. 0]iPI"c5R0^$Z*&!oa8|~(!?E{D _ pKZNwg~L_3nќ*0$uԸHfX Oկa*#VVM»_,?t: o +z,g8<0;.gF jb3k5lνgnl deJ.Mt|խì8m6gye4yBF6~hjp r~w~_ />V{s]a*tѺ^?WӁQQh@s~=HR)J2BS UBY[ [-A+snJG̴t1/`w~DqDD7Qϑ%XvLdFM c3 +bI 'EsCXGNǤ˒sjDW:!7)܍3q: >oJ|־mnm:K'R%c[ jX Ha6̻;v ]ڕϹ]0 \Xu fv,>f…+=JMULm9,C3FR`0V'/ȧ?vl}O^!ෝm=nX pw- \`lJ}bЃrՁ-9ISaBE+O+:JJ U0bfdG2Fs~-XF>Ӫu,|.Y*2~G"o=oI:Kn%k6$)Ţ)qțJ6 +Fx7# 1;VjkPy!4;D%I 3iY-O*_PqS=vRe@8/FN&|^7I>\AR 0HF.thL%dXa@TYUob4AD&l- ր#jM]XfZH^`F>(t+%==NJ.j&(K@S9Z+ R̺Y%KW&Mֻ@Hj8 hsaB2hH-|HUѥY2YڑtPtTRi2j 6ɗy Rk~\Y*uICT GsF[G lUOʌa`m ?Z +=A vnq`M>c'-77}{z|ȱY$,E)F0?GEL- Nˉxw+-̦Rp\p&O.u32D +n96+MPjҢeIׂJ q<}H:j:eF.l&"MUKYadaNudm;WCht7I)S Xt(&:yv>Σ zR؟%Nx74F= `Y9 Jr}) Hʑ?Yq}*qLN `.|G} \bZE<^)_Gp0Kfv|2"izTʬv;_Z)&p} >|`kn>LyJ#ڏյa*Zn^ DB,7hP;#͕$6n("bF%*REC( TA恪>`N >xEҎASK +83լ6G^ |k8Y j_|s6F_iEnZ(ht* 6Okg |ʯ p72׷-p'K.K/i<aO0q3z`)| {KNR56.*;EjH×KZl/xSx'MrKN$%?uI\q%/SEhą:iGs'v79eol:LZOͅ`"qơ%8IDV)6vmmne=vr8%l尹 +Lt$3^fR12qcsKdJy*uc+ߺi.HRo4:q3MQ<#kYRl,a/'%*oђbp S#RT+}vNr|l,*LYoß7[w@Ͻo^mo66z֊:O>__T7Uxleބ7<D& +}$81t<"-E89<':q0kAw䡑;I{JE45|)4~zamVsժ*&yBkrۺ s9S|eQauXhGt;_жa'Ϛ^Ƥ,W(Fb4| +`#X{ʩ\*% "sg.G?FFYEr@=(Oc֚LK2o0N>%`<ғRɜŸW,'y2UlwU&n p +Wm(>qHEL1AelonGϵ{pѤ&(֕&VJ߈gbPVLjb"\-`p~pl{+ȏ_!&c#u& foaI=%hM'?)16 eگa67I!ySVσa(2U̇ HdSDptXLlp\bk#U w,QNUG)n]F8;&D-qx@' +*2_hop͋+u0?dz >m#wn<bt}r%b8D&b}nLn:`緉Ug_2Bxv UYZUQX(UѧoasxDcaa~k?k "ްKZiemTG?-K&[-nġrpXx .!NV侳mwߛG<*+եf89!ʸ="h pAApC-l*B ,44 4ʪ45'q3ZLzU{Dp5E%\ +N?~| 2WRJvt`lKz )$M}8 s_fpmAX̗ 0`}Ҋ|W#x>xX,g5q>`%#"2[,L.tv(*ȓX_X`)u)N<iH6UϲہҠhQVhm ~BMxJgd%gv_W* aw9Nevk1xc=sjV1<eh,Z[|7qP מ2)T,>8~oҁD]1HQy&/+)DQ3RAtOG.&P vDbQDŘuӎzTvXkY[BZliXx$ w:өܛqٴ!Ҧ>+4Yߕo#T`OvF)*gGU80-{aD Li/?X'yOGbIZmD7RR"zK;=nKsV4p3#ǾL2a k) e޳EXK`l*r)ii}8 r좋an7mS.OCFZK~Ǧw8'z(cPD #a,E0ƶQ n{wi(V;Jpq“sAڎ@@B-*'y뮤50i{6䨄)&123U7hrJhtC$(*Cĩt_N=E.0PE0Ul0HU< VK:12sZrT璲HnIš$}ykmUq NA+Í0h1Sx?{x +Kv{*4[ez`no"ʨT%D ]2mox{IK*-͏cxaj5ߥ1ݖ83 s +RcV¤pߣ.-R8(N;̈́Yɢ6przu X.koF"jA+։_+b+)3qHA7 KзUW˅GV9y":?cN¼iS[y`ۊ˱ӽr ݯ!ғz!lYk^c,x+ZzCV1 yXU4T>7R'd+q5dBCz6[As1΢Ƣ s+oXxz'ܬő~-yS.3]t9&Pn!RIЌlZƹ?CZx L4Wg>߮eJuF/n]mW9HcyB"]m^ґqEx> +endobj + +729 0 obj +<< + /Type /Font + /Subtype /CIDFontType2 + /BaseFont /WDJXAY+TimesNewRomanPS-BoldMT + /CIDSystemInfo << + /Registry (Adobe) + /Ordering (Identity) + /Supplement 0 + >> + /FontDescriptor 731 0 R + /DW 0 + /CIDToGIDMap /Identity + /W [0 0 777.83203 1 1 500 2 2 250 3 7 500 8 8 277.83203 9 9 500] +>> +endobj + +730 0 obj +<< + /Length 10 + /Filter /FlateDecode +>> +stream +x +endstream +endobj + +731 0 obj +<< + /Type /FontDescriptor + /FontName /WDJXAY+TimesNewRomanPS-BoldMT + /Flags 131076 + /FontBBox [0.9765625 -15.136719 638.6719 677.2461] + /ItalicAngle 0 + /Ascent 677.2461 + /Descent -215.82031 + /CapHeight 662.1094 + /StemV 168.6 + /CIDSet 730 0 R + /FontFile2 733 0 R +>> +endobj + +732 0 obj +<< + /Length 731 + /Type /CMap + /WMode 0 +>> +stream +%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: procset CIDInit +%%IncludeResource: procset CIDInit +%%BeginResource: CMap Custom +%%Title: (Custom Adobe Identity 0) +%%Version: 1 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo 3 dict dup begin + /Registry (Adobe) def + /Ordering (Identity) def + /Supplement 0 def +end def +/CMapName /Custom def +/CMapVersion 1 def +/CMapType 0 def +/WMode 0 def +1 begincodespacerange +<0000> +endcodespacerange +9 beginbfchar +<0001> <0030> +<0002> <0020> +<0003> <0031> +<0004> <0032> +<0005> <0033> +<0006> <0034> +<0007> <0035> +<0008> <002F> +<0009> <0036> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF +endstream +endobj + +733 0 obj +<< + /Length 10723 + /Filter /FlateDecode +>> +stream +x| xս;K,x'3IlLl! jD@bH (j\F[iUZV[&DsԶZ +*up{ުdwT}}{ɛVp}?`m]mƲ+nnշO9k6vG[˪36ut*ˀJW^kV|'*H ʖ#wɏpk $?vw2,P+ںيlra6T?@- t;P5dC" lQ:c>LOFX{B$`7]z +u1$Q0 hn.$`'@$wMЃ[pDpaw=a օrDPa=J!Ԍh2Q,eBbᵈ'BυuCQCAû +:"a~}xh*@ M{x6fa.ný8@z؂t5 tkSk$c JjW`UXM{ ϑi]KBo[|w#eS'] m'Pyk8eFѵt/]϶} %[9zF@ )wxTMGv=0|3t-(@&< AGN*t5]Kܬ=;BCB:2Є6t`5nx6> +h:Q}a~)_οhǔ_(_ 4|RN!jQ&\ xTH4Sv=LWtޣw`cٿsx%++ZN~fd(*   ,!e2V܄?#؋ |JFdJ%iR]J t] ޢf.b9lϖ؇3ǯ?/Okx)gξ9 +M1E2QK + W\q/`80 iy4b@f2m/TAQ3k{hG?%z^c}]:vkbMl[ɶ^eoO۹'*~q?d)>eFkkgXaXi#_~c8a24k4M3ՙM~b:hz2O"'R*M]2m (f{8 +x_,^g7X9^0ůX +>{=v$g)7+/ `ǘ +\Lr Ny6QU71K6Mx ؁h鬗ߡI-8q|Zl9+3&Mƙ4ЯE7(% +0AIUT4"(cX'7pHg r^$Vc,=X"k*"gx\؉;q#?Ķ^q8`4*ƣ X՘JP+1CWa< Zhyhh( !^܍Kc`x>%Q&y6~jkxq +M؅6NS$9i%^E1|hZRЍW(Gazp5n~SdxDʥxcz0EЍ4Va"r>z+cBҮ\Et \&MC}(BTgd q +8L@b^:!J@1؍zbV,i_<3<x8ųxRXaCv*ҭ_\Ι=kfɌSS + r=9'eef]NuℴԔĄXfDGEFMF!]g5,ܹynqZ 4-N4~k;ϥZQj:6JI6luV+ -[vUnt$\+TrΪJUM}U͕yYhEdT"*/Dww?%Xb~%/ʟ⮬'+~Yղ_25=1/O+ݭ~V$AT7VMRSx۝}wmhmDrjY-BOtW94;*nM}UIN߽a<6]rr,گ17/fsy৛r_J4v#-NJK6RRHr7eƖʴ8-<9z`ca 2hIH fhdIXךΕN?4,s@j U>L1.L1ܑ11(d2=N(S(<(y~wdW9kSK̂tۃZr[7}'ZS +<~,0#ffٝSJߜ5jK駄/AU}ԟ3Fqa[SYb\bĢW2JQɼA!gOjh0tT~[sg(wӭ5u鲆6yk}Ch.oϠ[7webT StPC5K,SkVU N_aec6]QTaeP1A>US2h`H~Ua|=I֘0J iÄ򽌞5ܬ <iR%$g|LIۙgg/]{v6fmlS +t{:)? Ne`:0tkF՘19єLa2U A 8 9`8MZa21Y䈉7!XoH`La0a%yfvYXJ䤘`fDKcN,nj]k;$jQg;mT{qL+V=;-))΢yOCǧ}*733z+{tg :w[OȨ(!ձ;aoKHc,"iAj(H' 0rWG%g/{G}%\WK}17./`( ;2 q(i0[- -l8@&s`@$if2-0|GDZRR0*LIB1 +zc;T{zqeӳ̶BrܒQa{j걉"{Q۞nOn/b7]suC-G!, >`Xi1FLF6W"S <e4e-52mB) +纗8尺.":N gHU&$&$u2 vmo|T2'Wgxi÷XoV;S̱E_]@`Xa 8i@TxcE=H1d B'/2 8N Xd0_Z#8g* $DwOde`Pfq29)~5EMa)b9KIoP̐%CE->?5326#+-uB*3:2c2#]4ў +JVJU[nQ[ړٶ Mjjf6=hݮ,6ؑA8ص/8ŝ;W~o(5kyن11"ߞkn#t{gi}řkNqR&>FI˸bud<;SEǛ^k-67!E)} +mDtIHcx|&ο[ +PULߣP:SgeW#l[${*`]9VKZL({f0ac °l0l@;e/a60lF! &0 [b%p08ð|l K#ðGGF}aXAA ͎߄aَ%lC0 7(Maw}aw}aw}aw}awI8R^7H8 + 0H8Z>d?prF\VP!a;2ðoFcE 3 +2x {2 +3t08Sו 3aH8Y+U40 ' Od9R}VnV5M"YaXAnZ drD|I9χa!GeeW8q= / +1D=:'jхuB/6[T --Eȇ>Z8E쵡mmX%)-`.І")}G,@ 6  '֢ W+JtaTsBtJZsтuRJ G:Nb7D:k=2NX+G>|t=uJ-+׎^\F*9'zG12:B#b;KI6\ViJ:){RƯ{4c~|/ZЉA>щ+% þ,A,Gb*eNdVڸ]q/1c:eu1:,E8/K]̄{# kHX̙.K +X+jG{8N2m*j@v1O.:3pt)(AɕF5D5 >1e;eĄJUGvZu9.MZJ B=WjX"el8*U!a}nH +etȊygN\%R_狙X3z+Va#kc/xJ(|Ίq~ llǯG#)r#g^ozuσ_kָ7B*t*y_V{-T +{b'ڑlbǔI!z{[e܊]td>Wx9sZX̚W}-WL@b5r:т|+P0+\jrx=4w_gd,0ZͫHՈ,XuHU +љ3nWA[XY%.mkXD_HuQ]h F+eS׳ kCՕ(c'4L8r;9!\zDu ֙/%+d(Wϭn#<{ĮgޱY3PWq"-=C=VJ[tJg-sXθ՟uFգ:~׽Ӝ[cxf8guȌz9h9 +vX_Ev-hE\q)Ge3uwM9KxΕؗ2a{Q/$毠w;?[#\TI"T‡%rd)%]K0B%$K>4ptKBԣQqr^({5'摼Uh:TJ]%Rv-NA'8*RUP׷ŸE]\䥣բ +KPa' j /:lOHH2+p1ȞKPEX* ɕakJe—*i,v/1Ӊ\ƈ `8|2"KůPF)"̕܂f̢NY.7敞)qA%|O;kQؖ%㤝K%~JWȉdCX/<^s)bEDT#թDn2R/#L1.""s]Y%gd\\ԽH{l+Ӆuja:~+?ğCB<=1ͩק$_|}J?D_9>)yRgӒOK>-'&瞘DSOM>5zj:5ѩųX6'^l 9.F={X58Mx‰ʹ#r~'1vDžrt*|9ЅyΥ+Jk3t>YP+RR*i ES(5JɅ$z6F}39"s~FQX2xqxFҺ [ +wr|E^g;1+qJ,fCUbTgP6 *QN)lU`*)PNG"-8fެ֛")Ł( + *$n9BeE/BIgR )@bE@eUg bѭرGbVŎŎ!EŎ;;v+vRKUlSiUlP +8۫~y*f)f,R̸_DVT@xeȞ안@JLT(*1i3t` '{'אB5=B > + Zz5u`~ ~0t񏱅 {yS"g 2kI8Il'ɾOJ B_V;(,ց[_/c"T+(N>|:" ^܋ ?B{QȽ^#V> [4xO)>M,5| +ɧ>h3;y!ͼy!x6^`+/n^A^S&yqJv!V?1.^sk܉f@CI[-|ŰŶn֦fz/nٙ[#xaQm& 4(ƙE׾_I-wLnnb}d1$ql8F6$v ?\zd)?\{d-?7O;ǵԙ+НT@eowrE/JsTw(^EEq[3m:eGǍ:c۸ոݸhTM2fTN*Qv`ʎbl3ȎQB3;nQI͎P@(^a+{Nu>{n n'lgc\Ze832T8l7&| ~5lB {Uҽ +?+{%tf*%WQ'!7{bӭD lgpGۅ2 ] + +vLn "M4.7iHNYNv SN^hT +9z3蘯퐦 /eug;p\Blv )Lҩl(vl+-!!nvف@C ldz/qa~O/wmW#:%j-&n2,?ΰwҵ.˿,O,?uYrY,].!*[@3qm=MKtZ>sZrZ~bZlPl9tYe@NaL5>5RK K nP,6P{`VW*~h9 s r5 5HÁO }hLX Ǣy팠S}>X cP)-S2RlPh '[ # 5HU ҏj ߥ釁jv +y;-܃,jS  + ݁5H]کjJ_T(=!X~ ڑ#-m AZv S"$BBUaG*BE +&X {LR9mw *WڬɪU.%<+ fiQ=9YAaw'ZzsfmڻԞ jwl(QYv8+'mW-9wSŗ缨.`@i+K Z&Z:XܥO=(b<xQxS& +mMLכZMrSi)Ӕnh3;6s9i6f0C5c8E"mL_o 3|cy YZ BK3<5~se DnJԴ:gx.'5I?5HoRHpܜ*g?roVhoVc#6%9J%Օ5U+i,z߯YFW 5luU~F4 Z"Ʃ?K܏Z46gQ*Pʖ#~VUD_Q?[$DT!*>]a[+jiloX0ǰV% ,AͪZПcl33J~ AY,j+'h`Φu kUmf:[[uܺC-mMJ:wNy =]ُ20GSnlXmst6kƶ&?z@/ ]녮BfI95QX\oXT95<]*άS(GiGwD|@bWuQIJO=@Q6w.GoRUgeUgeOOOOoƍ7z<='=U[U)Ga|/6]NOECmmURge~iǣ+x#C>'ȇ(cBj\q-OGȎ)l8B6'6)=^koy|0H<>=lPH==c?==ŰoaG#20=='@3=:RK!XѰ0B/\(> +stream +xuJPFOUvDD@`]\ +FMkIRB|&إnऋ(HrTų9` ըa&ʺ:l +3Ŭ*ުnh)&C|>b纝黓AvCƫ+ y') +̵8+/> +stream +x}Kq?UX 94%MQKSN#ԦQvBˡhFk ! +ՠZP~xx^ޗQx"^P c!H0 + 0l+ߣy7;׫;Q?V._tF3LEK)y z80eIPkT/%[p:8+%d"G `d_{ٹ֖gzm\8rSif1\CU` S] @o _ +endstream +endobj + +738 0 obj +[772 0 R /XYZ 85.03937 751.4504 0] +endobj + +739 0 obj +[772 0 R /XYZ 85.03937 611.0499 0] +endobj + +740 0 obj +[772 0 R /XYZ 85.03937 309.17987 0] +endobj + +741 0 obj +[774 0 R /XYZ 85.03937 660.4874 0] +endobj + +742 0 obj +[774 0 R /XYZ 85.03937 174.13959 0] +endobj + +743 0 obj +[776 0 R /XYZ 85.03937 382.9676 0] +endobj + +744 0 obj +[778 0 R /XYZ 85.03937 569.05664 0] +endobj + +745 0 obj +[780 0 R /XYZ 85.03937 708.9526 0] +endobj + +746 0 obj +[782 0 R /XYZ 85.03937 766.8504 0] +endobj + +747 0 obj +[782 0 R /XYZ 85.03937 213.8598 0] +endobj + +748 0 obj +[784 0 R /XYZ 85.03937 420.1854 0] +endobj + +749 0 obj +[774 0 R /XYZ 85.03937 709.9754 0] +endobj + +750 0 obj +[786 0 R /XYZ 85.03937 766.8504 0] +endobj + +751 0 obj +[788 0 R /XYZ 85.03937 751.4504 0] +endobj + +752 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + >> + /Font << + /f0 674 0 R + /f1 662 0 R + /f2 722 0 R + /f3 710 0 R + /f4 680 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 0 + /Parent 1 0 R + /Contents 753 0 R +>> +endobj + +753 0 obj +<< + /Length 459 + /Filter /FlateDecode +>> +stream +xMk@+Z!ukzH$?U'rwW3nX+$U OHHxȢmɡo' +}v  TKBK,f?-a_KV۬vYY:\75Tcp!l@Iɗ2/}z_(x1 +dl>!0k \vO5|tHrmj"Edlm v "2]ݺ &@#\*2?NNn +C +@0I5ma, +8& +ͯ<ןP|RS9#ޏ + O>,b~+Fvh s>#o_XH(rt:m0W# +endstream +endobj + +754 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + >> + /Font << + /f0 680 0 R + /f1 722 0 R + /f2 710 0 R + /f3 656 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 1 + /Parent 1 0 R + /Contents 755 0 R +>> +endobj + +755 0 obj +<< + /Length 1581 + /Filter /FlateDecode +>> +stream +xZ[{7}_[YKKӒZW%Bh׻3Q /GwΌF;zpnݪ=ܽwj{٭$L-ɔ\j׊ V`w`&jg\ 6>FǂIЌO'Vq 88O([nhZٴiiZ۴(W{j{[V&IQ{JI&:!C6nu3t ]7yHW7>&_ԫ@R$kĭ!F]:pmKf?M} Av0?Ur-hCXKTҭ.FJ&)"PIw/6ם$|Cr:~RaE-eӻvK`"h`|p[%IN}D(ō ka_ +=&ӖKmgx\B9nV/'ǫ(<zdTh x#$0(Ic~u) 7ud$Vl0Iu/[ʰ|EYz_,TVkSg<84]mqaa0x⎌t 4x7P1ƒ1@b]^#Ðȴ Ce3fOC:eqWݕ'+)ZhR -Z3{%:I&QW0bBSL 㹋̃Th]S],҇0+sL|uW0\8# ahzNV߭U`b~knn x4cumJPYmh@P)*p)9[dSw[9NVн믠KãӾN[P֕9XwSiNf=g룗hl6;;} +>쿷Gltlvt?GN)zNf.0 +ڞh&e}#.b嵆Oo +endstream +endobj + +756 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 707.0099 510.2362 714.5724] + /Border [0 0 0] + /Dest 738 0 R + /F 4 + /StructParent 2 + /Contents +>> +endobj + +757 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 692.29736 510.2362 699.85986] + /Border [0 0 0] + /Dest 739 0 R + /F 4 + /StructParent 3 + /Contents +>> +endobj + +758 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 677.5849 510.2362 685.1474] + /Border [0 0 0] + /Dest 740 0 R + /F 4 + /StructParent 4 + /Contents +>> +endobj + +759 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 662.87244 510.2362 670.43494] + /Border [0 0 0] + /Dest 749 0 R + /F 4 + /StructParent 5 + /Contents +>> +endobj + +760 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 648.1599 510.2362 655.7224] + /Border [0 0 0] + /Dest 741 0 R + /F 4 + /StructParent 6 + /Contents +>> +endobj + +761 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 633.4474 510.2362 641.0099] + /Border [0 0 0] + /Dest 742 0 R + /F 4 + /StructParent 7 + /Contents +>> +endobj + +762 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 618.73486 510.2362 626.29736] + /Border [0 0 0] + /Dest 743 0 R + /F 4 + /StructParent 8 + /Contents +>> +endobj + +763 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 604.0224 510.2362 611.5849] + /Border [0 0 0] + /Dest 744 0 R + /F 4 + /StructParent 9 + /Contents +>> +endobj + +764 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 589.30994 510.2362 596.87244] + /Border [0 0 0] + /Dest 745 0 R + /F 4 + /StructParent 10 + /Contents +>> +endobj + +765 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 574.5974 510.2362 582.1599] + /Border [0 0 0] + /Dest 746 0 R + /F 4 + /StructParent 11 + /Contents +>> +endobj + +766 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 559.8849 510.2362 567.4474] + /Border [0 0 0] + /Dest 747 0 R + /F 4 + /StructParent 12 + /Contents +>> +endobj + +767 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 545.17236 510.2362 552.73486] + /Border [0 0 0] + /Dest 748 0 R + /F 4 + /StructParent 13 + /Contents +>> +endobj + +768 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 530.4599 510.2362 538.0224] + /Border [0 0 0] + /Dest 750 0 R + /F 4 + /StructParent 14 + /Contents +>> +endobj + +769 0 obj +<< + /Type /Annot + /Subtype /Link + /Rect [85.03937 515.74744 510.2362 523.30994] + /Border [0 0 0] + /Dest 751 0 R + /F 4 + /StructParent 15 + /Contents +>> +endobj + +770 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + >> + /Font << + /f0 704 0 R + /f1 728 0 R + /f2 674 0 R + /f3 656 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 16 + /Tabs /S + /Parent 1 0 R + /Contents 771 0 R + /Annots [756 0 R 757 0 R 758 0 R 759 0 R 760 0 R 761 0 R 762 0 R 763 0 R 764 0 R 765 0 R 766 0 R 767 0 R 768 0 R 769 0 R] +>> +endobj + +771 0 obj +<< + /Length 7700 + /Filter /FlateDecode +>> +stream +x՝]\WtDVE ;1v"uBVċX:`/[ۧg4"͌5a~gxy/g/t_|Gr/_ǫz~|/}<ʡ~^Lzz6)zxݳow𷗯~WϿ{i9zx~?RPJ=}w;}v}~?o͖=6z-rGw˟߹6#׻~w>{/^G8ctHWP5vH[R/G4Er? = K)c¾؎wx?;|MWȷtrlSɚyEYd>/ԫyEy3(3;s +ZʼZ1c*-ڙGAcH+2IHjy=ez+1Xs4\<1oҺ3{r+*!BNŜjaI[RUrS5 .I*ޔٍ5iQݸaW&MzVc +fTJ)Z;"E"\3i)nڍwڍh.6%%{Ȕa0)#+E eKx"4qNːfz5 $w^^ܵJF(B:ד):{rs1q+y9!%)RRBsf߁Нً;Bf자zBJ͘6R´¼bj-GLX@J̣*GFJ2'.'XWNԛW/_KѢ%/8Yo*oڕO{Q  84Gi#ͣR.ܧܣunqh/݂z>""~dlqe 1DdlIHLf!KCb26X"HLƦ"gd$"cHLƦ#Gb26!،$"c)IL$KJb26+ش$"cyIL&&Lb265$"cILN""6=$"cPb26E%&ae)1dlD%&d*KUb26W8JDIJMW"26_؄%"cKLTY"2d%"biKL-1'qXqRx.1dl%&c󗈌%01GHX +9LLIb""d4&"b󘘈Mdb26X*LDƒfb26|&&(4 hb2NJ9M7f\[ƮԴۂTwKEҟ"k Zt$bhCl.T$aH<hC, 8[H<Ēx KAN px?~tSe|o#ߵB%ޞrZ}mW?z@ ]U鸃`r^{z}#KjG4fjRotkHҝ5zJZ\F*Hi֨5iJ0oֳp!T"m8DK/a3s< DB:C[&F}vɰ6xfm"zT'2tLCr`ZzZɑ*z4}r"ns…piHV +c-6OC; T"II>91 +1%1o4tE=ܘ5COntt7Pf>찵y "2BJ11H%DO_H1aCᰠtLy hM/SV*#%cPsrEDF1M(s:e:gcI}8+w*N}8:Vmu5u|r^jfuxJm7 +?}L\*SK۴~kz+KwbP^o5Dx=+4'ն^SG>W멭qTc0{|v՝oocf\x=$ m0H[g"fL`t<rLČ3@ +Da 0C !6 LȀ;\ubFQμdg@;D"%P8o<߆ 4 g%9/~a`x |byľHh:x23d,oDCwN|/Ǽs<^$4Œ{Q;G@'1>QQ/WfivT<r|b^1-G1ev341e>QO.zw+::+3E7"[!fwEKDIJgn %q;P$20U#10U!MjIlJdk &"1<$2ݕyQţ.aOT&2 tlLcX1FEdta~ZeBDj<)Go':1M6E6qHzx/jo\KL u2SPpޑC Ww#/dExx ~41xUsyUG +λ;!4D-ݘ<(虘E!RER;]qGªw#%-ʬKC8]xK/S!ZT% +a(!VO<<ّeW4BW'=PA}ePUCtKji^@_c>`1+Mb40ĕn +g0K`Qg$RR묚Cq4Ïno^^J9p١Yo|MG@yI6ɜ1KHc$#P`(sb3QZ3k뮈r&MOP6v`ͶK7KY'$n=MUjL~u] ~//?ִv)Ū7vWX=}p=-O˷~_lֱ~wiZa4(Q̔{xueNRjł"#VԻ'f2\LjԷOP{ +ԇǀwa'21퇇g.)>h欻MgOS{Wg/-ȶX[Va |rP_Z 0DCT˳Jm:SA/Y)xQ@C /Ńp!Y&"TrxL֠(9 +6y6++1c " kyeO0 1,kσ!tb}]FB*QH L^1QLu` Q!f꘩ΔZcOY-iB2Ω + :SLu'D 37re:SB}VLEpyM)~N czP)zUPmҽe]G-x$(r/ =%tUM8{BD& +1Ϊ\!, "2.c}!"c C<2 'gFxC"25c!"cyC E<("2Rc""cIEDfgؼ",YDD,Xn"lK/1~#"cFPb֞p['I +endstream +endobj + +772 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + >> + /Font << + /f0 704 0 R + /f1 728 0 R + /f2 674 0 R + /f3 722 0 R + /f4 710 0 R + /f5 656 0 R + /f6 698 0 R + /f7 692 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 17 + /Parent 1 0 R + /Contents 773 0 R +>> +endobj + +773 0 obj +<< + /Length 4265 + /Filter /FlateDecode +>> +stream +x]G@~?JY@dqwռZӽ;kg뭫k޼zyzxUU77WGoT_/e7W_yzuzZmζB!UlrڇWz +WϯW˷G/QU/'x +Pn_MjW׾ɧ/V痫v |`wmP=zeWra(`*TY=Y}:pz˫an?8`5g6 $GJ"P8ekj$0sѨ|+wCR_UNZ@&)t+%T{oFXWB;7Lݨw^Mr|0ūyyp +Q`7!%俅OZh-Iυ|. }"oc}+of?7OIrlSK(8mcc p8yP04,@`\l !!74Z)]J)PE[kkQN(.̛euR]yCU|i0h 56@ cFR "@  D-7;04Nm8R <et"ZțHV|,-RiKƩjl.*ŻGaie"6,H2$eZY\x8[iQ8 sb\VSE&qOch當6줖^ki='YV$~Ly$~V.M ([ ?G7OHBkA5kq"v5UzJP+0X˧z>k-]|t]6=4!`F<e,M2V[L<^U[Ôi!݈*<&fІ4S\dt] |@.Le?~}nd.\dgvSa9Sne^3l5dl t-؜5K;X~md6ey26krKqiAՃ'I:*ozpGg@c( +\4ʄۅV# t嵾Ȏ" $ +!?xuBg9'[-\"mVvBeauͩ M W.j˰؜TlU dbX,P>.Y(!kJĒބfuMF0R(CT>w]}N~oB/˔/xw! +]f%e B>j:>!YɮzYo "fRUjϊ P1g픟Nf*NP)H9M Ь_v,Ĕ#YˠJ;(m ^y|!O;+v_ `ߺپJȟwg=}CҩBɛ60- +2ͼ>K~W1֊oG*Qz_qr+fyH\\>!<^i,JE)wJw~gI}E|v h(~1J,R\-&-팹%6+֎},;ud$YRT^]`swWqW-=@QcNfPtzCTT2} {Yƨhnϲ%1:+t2}4G^Vo,R3{Y$DM9$ .=xp&oZԨˮ5%5PcSR"dG>;:)sn|@x3LV|d~-(y)4-tqKvlxR6A9T|(609E4=T#{_eR,dRj5D눇ufdV\w|if|X8w='`Y쳔Ao7q1˘@G&SCməS990VMHY@ےU&kT9HJn]dSa_uвI0}-+Yņ],$5K=0lM J*/ۨ 2aز- +D e\D)a5T5T,F(Z+ΘoEG5ͧ^&%;&w)ۡS@)oPlR+Φ[ 2[ lVNU*@خŘ?z'OGG>U3ey1fLU2|I0pi5m/[CJj5NAB2"q68 E&cgQie47wƔMY4W)c\&m׊gh€M.{ֆ +aa(K,H][Iݳ=;}I$^="8;E d!Xds_;C' 9,wRs}g~ ˮ;>˯fUeXl$+cPL]zFqVM&MRړ]F"D{25%e,eE%i}&x'6)00hҡAr$-%?$+F\oG0`Ά} 9)5q6t~hr٦ .nat+uH2g]9Oסj_gjʷQI&Qy$ۜ1󿾎6 +L lVwM |9/˛@l6%[6xmsDlD*e,='%Ks^kW7̐nWƶDŽ9%)d,J~$l+<_y' EJݴNAH8,{YYGL-]Sm(@ɦZN9ϋך ~!J{S&%jhTFӡx>xmX]]a_y}S%XɶQu5ƼUVpJ;g]-bdmqt;g›DMҰT 5Mդ.dzŎO%nդi)ْW==o7 a%]ǪUYA&0ָ6ҳBYիH7MUvU?GFJ]S=T) rAi",4&=xt(7ONJSdO O - ; џ#ǼpAr$:>r2s+wl6w  ;>AT1nv\-=JCuhW؍fKv(٪HIyѳ6&؆9sRYHmRJH}/\Hw'z^jl~%7.o&RlB,{ȬXfPB.nj%u/"ݱEsWO׿3ʨoѽ~zJvT 1,3q\*J{)CRZI3t5^v^usܧLl|X`MUo~'?I[XYcKŔl1iS#ȴP:ɐ;(eP-98.H$FHIͅ >6O.^1|" uCSkԫKwcL0_ۼyz.޼)1:iL+˲Ej 7 Y +endstream +endobj + +774 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + /c1 735 0 R + >> + /Font << + /f0 704 0 R + /f1 656 0 R + /f2 692 0 R + /f3 728 0 R + /f4 674 0 R + /f5 716 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 18 + /Parent 1 0 R + /Contents 775 0 R +>> +endobj + +775 0 obj +<< + /Length 4484 + /Filter /FlateDecode +>> +stream +x]r$q}(KkO& 7{,))~85E=hvuus".R~":u2ݧwݛ7;[]~.>CWwcwۏ?~M.O7w>,޾׫.C5ulrڇ?/>|Y_,].ήuϋ~=]ywջ]vW^bs!GG;Xd tW0ݷ?/ [Eӡ⏋ͷ_.ov}ݺ@@1Q9b!\{B"J_Oo>޼@Pqoc>>KXc|+8`3@wHKSh Ұb_u`DA9Ov *dfyDSl:ȩG?k?vQ2F9͆'p*8*9} C@YGo xvf2Zf|o"^\vbGbڋc/V;q=iD- +4vmJCX4@\ui y]ޚ + ;@oXX@Ky-0 Xk@I.ƛ~ݑ˺nꠓh0f${+Ef/eîR&a؎9YEp# on]F'2N9$t(V([-E4au]x' +Hq; "ejaJz\sE! \E5= C+)  +x!IT, \ŷwͫ~7{l0sY>|Z5C zpay?m)zKՒ H^k`Hgdڵ5 \%b˓}ȓӻqcz8$Cop*wU8+R*Y|Nyc4r蠈1^J䵞8Gbacl$SG ݽ/g[EpT9{dN(jB5{KHNhm#d%TC(If:9eٸꮠիZ.V}/?v:U+NUZy:CZS^b%nBUP (+*PQ1`QpyG^I@wbt-L/{g%8ϯ\gU@82ym@ښgH8 x+:^ҤҶ+YaqҶbz(8/~P$n1oJg퇉<V@E ԩ8p>~_~{W<:kPWd,lx{JA. +>bKNH%jXz~̮7T? +Ϗb&uɞE֦ =Qi8LI-r5Ωg +[ct'Xzв8OUQ.a|w.wFNٴ!صBظavVX!nb+GCWkpWx(;~L:@>6ݓ1QٿA.j8/g|z<M1S.n:mwʃٵ}LyKlBjb؂=OUUSW YKE_F;ɇ]-y$Cu fGt(l'\ ;f$&Z&dM43-s[[bG[KvoiX"۝&<[QI@V d%|hxE0% `T'n|x; e}Ax~5G+j߷=Nc3uOQ aYncG䳵T--ES-+rBbD vM4޸`OR -zb`Q)(]ʱv/}o4Ip +ܿ?qjXl!i1Tː\Hlߗ<3bPf0'Yc(~&<#ɱN2Lmy̝2G>{*"fSa}L\ Q]f^tvg㞣VL'eo}WPL]rv8,=C}?$Q!ek{fB~.͒w5gEc'n0Zjk(nF0xCFVCAv;LwCI3$yXyeb5^+xt^UqL,MI56,:ѩ=y~F.2)M *u![eq3[5>[uR* iy;x +.9`pП>沔gyWk1%ck0Xܭ@J4iŭ +:z; *8[b!<φk,nL03Z٨~.t. .eqǣ%dkAwyamA+fL>}2e8&xӀ3f l6Ń='ɟKS&1U4d},%AuSW8, fEl,E(0r[A3O9ؓŭ0u9JR#>t2,}rM<{E%k-y~Fp{UaT> +w[vt>3GA| (pٞT qw;{3PԠ>Df̮ b{ހ"yȨ7q{͇}ڈavmE5kel +ufbjn[m o6v.-~m{IiE,6rn +\n;ҊڏԔܜ`}3t`7ܶmriPDeqc?~a,6m3[hG)+l+mqBOL*&f}r +͖1zz ܝ@Y.I4,J32TbUގ6R0'Č tbDl9iۼXy`UX ` `,``<0g^z5'&=֡,ysDPfu{Ěl"R)mQ"U/9RDL_5v~x1FI?"{b54I7eE#&Z]EcbmAYө'zkꞿU7Aa $хɬV4j^7Lf4Y:s9YGRՒ+E;u +h1(gI +pA0s%lJ 3W76׎5z|6ɚHO= 8Bhl&[RUr 'a\61E)c2击?}Hwo|}|6ݺ~˟>\}|#^;ˊC.#D?P +endstream +endobj + +776 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + /c1 735 0 R + >> + /Font << + /f0 704 0 R + /f1 692 0 R + /f2 656 0 R + /f3 716 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 19 + /Parent 1 0 R + /Contents 777 0 R +>> +endobj + +777 0 obj +<< + /Length 4801 + /Filter /FlateDecode +>> +stream +x]MoIr(ޕדO@ lcB́H2<Ԙ=7*#ʬl/jTyǫa8{y{{/~?[o~OnƷ.~ϟn?}޽x180Г 7;zr# /ݿna^=qݟx(SbBCG`x{_;7|?Ρ76gvwgo @ $G" "yΎ?B>vH&Ia. tG" b `2 =i³" ma.` +"C\B"k,@X=4WmY`5Ycs +~!E~< VE9ĎLNcE`l0]6Vd3>T'i<^<#!KREęd~{hOj)<:+-^>mAP JlNj9o1%i8`]2KFMLs#[$U8o7܍WsҖ36qС bLrBT1rf-U*aGoc8]GŮWEdmgxQxWg8$lU{H7٠E@^B-֢p*bL3RZJh TF*FyH}g&& X35oXƋXIl/l h[99i 75\%Dcq{@Z!fC) +~bCٚžx4\҅tQu6vVgwʩPvmӫ${c= &)$rF"kЉ %[` ı cU'|R}\j_gͿJ4d,x"Q҉68_-+kg-8hcZl.CܴϋbɫȢܷbLϙvj!,5l@=ڴId^>XVe1Rhy0#j +(F +߯vfB8ZgX[8\􇮍)Z}c=xT?7>&=ueWkcAׇP3ZG+&HKu$N*&RcYYS7YSad;GU\{A41%;juElC+-媔-Ni u!OA?&{g2Z<39VG)+fɄm.xv̄O3nHY76>ckslc9-\5Bc϶7C I&`SZ|#LJht`\D+,'[5^f9j;"-Q@l:hY5 hN :sjA> +jIK,X-YSY\d/ &Xg :2 QM8VՂ$ݚZM,,o7,}<e[P~.?%s*>|/gCy{g=:qfy)ЛgOzuc#uY8HP&Jߡ*(ZjEF09:[~Au śS@.lZwN) R @gƍDpCO4β# ķ;N0(L*A7(Qt鐠^C5ٰ85189#.vja/a}Ǝ ƢBu9$<OKB47~~t!;}إS_TzTD9aI6 &í33ڻr#z|ȡ<\ÏLl|6`z1ш }e _ty_\> +`c동MLMMʍw-{Qq_tf(ƚ%Eꅲy79Sՠױqoe4HA"p>޼>u NCu9Su=9%i-Wۉeu!./&;!oSYoU8WVuG1e!κȈzO6ѩy+"׫O 䣒_}:fueh75>y, .XuGRBkA]]f#*'ϣ1v.$R$$V՚f)B`zF3V!^6>u|w|Ƨ3y * T值T’(6n8P"/_<~۴P_?7&m:yu=eTd@gRw;]bAicoJD̹NVMK2lҷ z'sK>O|g\FګM2atdǩ2,NubDGuTgO$ 5P=dӈNb$zh|e:Gl,zk9,ܝd*^L$xȧ^eBShX޳d,^-4KXdM~ +KGCvMQk~ ʘ*K(M8IQ4BX<6Nz~\gUD'be0~wK.Ogn4a}o`X.u鯬Bð\\dOaˡf`Uò?Xzѡ tK.}_lb,{BaLxI+^hEY]U^Xmͩbҽ 必3=ͨmF2`SB_wCkeB fbD^a"K#/g7>^ݣxy{{/~?{/?ۻۿa8{퇛˻tOª7S_| +endstream +endobj + +778 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + /c1 735 0 R + >> + /Font << + /f0 704 0 R + /f1 692 0 R + /f2 656 0 R + /f3 716 0 R + /f4 686 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 20 + /Parent 1 0 R + /Contents 779 0 R +>> +endobj + +779 0 obj +<< + /Length 5236 + /Filter /FlateDecode +>> +stream +x]mo$Ǎ>ϗ;["[$``vrpnCG3mUUHY)"Nxs{ju_뇟N>}?pu׿|vul[ۡ'(.&o vݜ7 tvW7\G'>>3n?ߝ]jCzy(SbBC@7~_79$Al.?ۜ|}?w0lLF,4 g7[ݮ$G" "yL?/]NUF[rH`8Ģ-v褻_Ab%&b$ñ`Hi9Q)Jŵ71giS#!E7OXOeZ$ʉUV2'] \^J;Dh Cm RLT.jN3wq?qL& [&gE!gʉ!fŢ1^Z?pƛ5sN 6h&jCFC =7vHK=L+TdHւ?plAUJ[#S2`sre0/>A=)A(_ln1/7nODŷD'7s?Jx 8sךzmA@|DZ~EѤ^s%i+Km$VR4˧@4òM}pm[6.:nz_h|X,?異@냘0hQLDcq>Ut lf\01/&<e܇l$d}B.2Hk@AiboN Mu>u7=o,M 62 tZ.V&X#m Bh2-KuZ`9֥V]_-歁*vw;gPĬƹЯfY!{+H.u ~$MlF/ěZǁ^f[.Tb]8-m69fĬxϴCZ@-HV;,mZ:cYtO^'2A֑폍6{΄l`4oo}:ߢ%Խ pi._^|_ ywp "?3{9GXP'K38gM>A&Xh|-*;z-yn} /BoITn; .<؅p61av ":`\Wh,Ѣehl'C!VA=q +2zv84FUR/;,\2)GLmXdl pmZOWA[#RNaᩭ1j²[)mMk,ւ6 +J)ȎR$ ,smY㶑cgfcSFȆ}~2ԲM:q,j2:>9P~aXs٥H鲼O!VMȜNxS; 'g ,ܘ =hq̅lbO׀>7.+ $sbl46m2 gƅ,+|\eus7m¾s@D,1g9ez%qZ6L^z²8ǮX64npo"#X3JT}7S`Gat+oHu6d>Ng*9cfl1*p&CrSAZxrJGOM"K,9?S`NXH jDdK9ѩ)aci%Ǹ>z%mƬռn&Zro~Gd[CE-`cWl W @)EW(_brN%/6|<)u4S!xh۠Ļl"`!4INK Y~x-Jw@>M::XQ +VXyh`xꘃk#rm䙫c7=cӭfz:ILΕI~pqHVH xs[,ְ:Cn벍` bv3d9CN`Ő9E^jgbm86wt< z/Rb nQ%&xbƿ8\diٵ/w06ŋrőUDl#;ytɓ\"(Y☁ +TGdd\#)DUHCl'Z7c<*BT݁Y"۩\d`kly}ZOS5Kc,|zʶb4ꪃ!KaKتUC>%PY=g!ONխs#jz`z iziVPʝW,}mʦ*T0{0~&P(McFݷ7k,!XbF +=^$#T#xe~.pmR/j0ۀ2k~  iF0v_KVqxx46kWL1v[ Cd ˵?e™Gp?ą,ߜkMUYmpc&еK@HDiHʅ1YphݺV{tZN%vV){}np~t5Wk"[Yy~MxFoz+GF{ od'8b,՜$?<`jL3_ cj>F9f'\^g#aG$1S3,^@oXT`HoXf>e4 +af Qj4r!oi%HN֙ܞHڟ t4՞W8xiρǼ\|D!Ǜ;!~?w?tןo??N/?}pu׿|m P`^JBC} +endstream +endobj + +780 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + /c1 735 0 R + >> + /Font << + /f0 704 0 R + /f1 692 0 R + /f2 716 0 R + /f3 656 0 R + /f4 668 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 21 + /Parent 1 0 R + /Contents 781 0 R +>> +endobj + +781 0 obj +<< + /Length 5789 + /Filter /FlateDecode +>> +stream +x][w7r~_V\P +D+G"峛=JYSva}Nϥ*3zP#SPx7ɓM=~޼۟_xoox{ۏ[׻7n7Ovϯ.7t};`8ͯo{ tsy~;ߥ__+xxxë9ë;닟޼e,Y/Eh$ t//gc61h.m^nynnpǫXP@a4Z{:Po@ 2}~{`ލ`׌Z;;;{v.HJE :t1P@5ofg^Z5Aװ81+w[>V6:SOw*Dc($ШtD+h7'ia)`Jż>:+!kJD\@!fHfE hl kU.Dp-Ҭ1 H28X5ZuJ44N۱"{u;&vHQ0.]΄#$8T k%"gD>ÑyM@PDm]@ީ{ǒ;%Eޢ*73c sEx]sUEhd7VJAL2Yփ⣨֜)&m伐dr\h֜%&U4U~%8[L*Ô/|Ud)IhzLL4YA̶12];;?ILƠsKBuͭvާA W&&)g]\&AE] Q0rY,܆(ɴVjZvjt~>頥h/:T1njNL4]I뷷ͷ ~H XW/JrqQc C}ѡQ^@ "@0`>?jz:cW-s/@C QsҦv $4&7WGMs&Xe-)Zowyڈ;d{" 1X]֟Z 4&PƂ.ROK^$oc'ieL@D6 9,mp??c1N>Мm33-Co~L ~YF.JH‚iTaY~Aٚb +M֜` }g%GC}Z ]\]\}X>Exb )MټVy}"FSBЗ/ĈSHy0n"uԏXeݍ_Wڲ&mU`{yn%I\6-(xp +S?9H !ͷp("O/畢UcNڱ=^"yU-SX**N,D`#'L:KbkUu|)ÚctmvD`7uUaid8C2J1F씀sʠw/l;:+'>QiL[ifF C1eL_33C&nloMTW]YQz_+ ܔ3Sڬ#,8Z3@nƽ r/M!b4U]P~֐! {&EJs 6;<~hH@tnh(Dg)dDC31;^.ÿ +D΂*FYBF%17]"/`l0T);D\]_mwӔD8'BmbUlmC.v:zh/v +PED+TЮQ0$P8/Eyc}5 b0N~܆ ?)`eC,c 6M_'Xf Z+×QM3LPNh*o"jlȒb TswI⭳1ij8$:Sb3s )>:B痧]bMnlyfpD@ ecSSƮ=P!.^4k>Pmᐡ Yӗi5K"K NhL8UW?}efuJ~H94=G8 f56ɺ,W_;h.녗aBؖQÊφwY㍂,f6z-dAc[-0ډ5@CQU?ډ%BTyZY4fg$a=Xm]YegϯRh^>eFh9*b b)}UXM<̕r9R;2CG11[*)7iD@?69di4'Sι/󳇾@ЋՑh}Κ<3"|%pbG)Xmrnsb00O6H}dNة|kJT""H2vJpB|w[% "H KN6krӤ! +YuIz-B0L5%1I4cѠr!2g,~Z3M" *ӟyybw2HS).˲5̵0<< r<<!*YyJ! <_4mQtZ,#KD*bvU^&PcP0_~yh=E=bH5Ndԣ0vsXu 2 +MU\O* Wʱh?@8GYnFHYj!SWA1CQZh͞H]63Q@)N,ŋѩv r!+%@6bhYf(+:ci L5:(mR8xj ooXN퐛abgt$|J#Fɿ|^-7"o^[:m {7%t#0д3馡ʥXcWyP-.I?扻C*\"?TƲzSS8ugXkς`x .5X c:S.8}?∇gmw5S X'+1AgkUB;I yP 8}gZ\= rͪFJ]C bǢCEZu.&U&jIѣ/{. qO, JB"TM0dS!,O758'`byq ;:% +)z[kWKM+,WNOG}\N;&9U:Key&aܶB˅ylCh1_ bƀseYaX1V8[A% ~m,(,,X4ZC,:lU,F\FkT.᳌lR+sA%O:QyX%hJ }`i֣ +(eS©RGbj֊(mwS0r$2YvrKI.t*xks}mHVyZa $Pʋu6(3!N` y#͟*B}T^ck4ټ+V_ۼk_ +6˷_a) Gs㌲?>g,5>>ZHB'bk6zTrGQQkw$Y(gIkskncwe!?<-R8$Ԛ)[ XИbrӍ*u2&-rW4Z0ڄO4X"jb7Kdk+1a `H龕dJy/ŞJwn$lG=Å +> 'cWm90x^f"V= V(gQPh[#/˛xغ9P琟$ARu1CRn@_^d')١Ӛ}'xqSI4ܯ1GC=зbBVbwwIN0hT@V2m%"̳Æ]9 gj:b~' +r?gkpm!d)o/^ J*=:wV7cVk 묧/>"&PkC7sg ˥OLFRT\.d OKR*0Hrg^^hQC?SMjsrjZ|0j*ZY~ԭ`ȏ{k7plG'*rׇ+aJ͒ˇь(2,ɸRϸzP +G+_N),_yWEG2M>n߿y=@ly׷?w?>~aO/zvac[v*@E!\'[RRh +endstream +endobj + +782 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + /c1 735 0 R + >> + /Font << + /f0 704 0 R + /f1 716 0 R + /f2 656 0 R + /f3 692 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 22 + /Parent 1 0 R + /Contents 783 0 R +>> +endobj + +783 0 obj +<< + /Length 5041 + /Filter /FlateDecode +>> +stream +x]]su}eν]aT2qJK̋%LiR^JN6>` H$N}y>~ϯ>}~zw?}iw7w魫ox{}ɋb~_'ԠA[jjV178yOh/N^VzիWzWzӷտ\\aY£4~b'81lEL'Nprr~a/[4?x5!1 El$`#Z(7KSκ8 +nte<+aӒU6#aLz]l@klBf~ѽ.`W4() xI̱vV!*xдY#&]>o~..?KYr.. DDsz9`F @X^З%}vB̓YtˏrYE3 ÀYt%sN̜~,%i~"v Zs?4_~DЀ@HfK +}-Q²T3t!(mӍrbiF1x xZ89m3L̊IGZE&1 +3CoJq6(U,mie+7^Ӌ_ZpAihZr16omR*֎G8evCteΊj.çde u/R|`h܂i^O(}|Ȼ/p̻p./WEMu3E . +m{iIRx (Kc)2h e:lPzmm>;m$S4f jjڇ'azT''>~tjzbK\e&`,0s/V?>eĞi>Se\&Xd]Qd, ҩaMPbv }뷖_\5}qRvJmO +V(*}6vOtjoDrRyb`PӨ1vbVyav T-<) qT1OCviC$ܖrFcwi9;k{".BB4GAlrb1A9@~MZ1FX"8CbpQ11]UOƳ +;)9-87=f]P:{mt{{磊`0ANΎrKqK>}46g$JS +c(~Aj[G&Dd%I%p|X2;Q&Tq$7 +G|bD}y_ku2\Q6>T=:͊=8ukKE7}~>N|+Q,a#2|Ew0Ldix5 Gۍ9ҝ2~RTq+VhomxGcO}p:L!/1JcQ]6pDkN{;E<+Qolx}RXD gY(?x@D qK~a*VrN; DxHC-Yv-wᄡpSW2v"Ul<31SPvV$*ztDi4;Vq"혱 _;>wv2"Qޟƈ/,OhW֠Emz/fJEzwtC˟-d ӹSTkma0@qWTɏJuWoDmǒ (G:QmG8/"|=4 MD{<`"lxLQH5X,{Q+('F}2ƞbxZz(JhOAˊN"TEUgӼN4O&߿{|lâ{&$ExJztBqma$CJ\$ģHj*(@,*H:#S!ǯ0H=(^0>"b+SHS<ˢ{#'Lg~Ef%my^Ĩ!5۪%ݐVWt)䆂 +䪸bˢܰQDFWt)џ_,DuFyH=8h5ܚSG?}n̨.o`*?o>m8@"?D,=exVWb,?8ze*hlY rO-!))L5 `O$/QDϨZ1-UTB-$fg'wiV([cHhd!xׁ1 Z7XYKg! ppX/@_@4"7VYO{uo{ XVlw7þωPFa}T9}^rtI҉8+ƃM ,^oM@^a7< _c&]EUؽH۽?f%?Mw^cׄft0lQ9-\ovLDa~c,^gE1U]wE&ۨhlf(5veREYcm|/z)>Euת}mUO +-@sUDf"D1u;9YX.WNw+E|018[3Ο,rfeMuj&(7xT8嬋Wt Lď6/tZ&vڄtFg{2똡bGN7^WXQKu;~10ϋ?]oh:mWeBY0S Y `,c<'#lyWxQ<͌в>(NMx9E-`<0s[=aeg׀7<" X _fW\!N_ +.f둶r4y94΃8?EY!/sjp8G;ueK^8`,VxV,#m^?p pY$"~R\vz]/n2z`"QpnxGr`Y(η {9`-o%2s~0~JkFJf>3`N/J.0c')~vՉz=Nr_x/>jP*u&A;-"p~fv/75MӧUVUЍ쓝ݶ^z% +endstream +endobj + +784 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + /c1 735 0 R + >> + /Font << + /f0 704 0 R + /f1 692 0 R + /f2 656 0 R + /f3 716 0 R + /f4 668 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 23 + /Parent 1 0 R + /Contents 785 0 R +>> +endobj + +785 0 obj +<< + /Length 5248 + /Filter /FlateDecode +>> +stream +x]]wq}h;Č`}3V#T>N6>>PZi, gGET@c} ERB.p㇫wóg'p_0|{oۿ~8W?z~x}uˋ;Ɓ!!:hx;>y77'g`@|q~{ɷ;mynna\9>!D2 ߜ_Ē3| {'ߜzK7q1@1 zk>7h9d17_~p&B?'ޣQ@&1;<,ٽ؟oD2i% >†LJmm}G%Jl`f7ue `ۛ٬4Uu +=^x$l2C Ǚeݢ%xMXovPJ13'h1z_V!|=Eo;lB*.)0\`D813[6:\X"9^d~x[ +%6dfY9Ck21'2L$o)O]|Vr$"lN 圕Nbnm$tȤS;6O)J .*#`߻:8jܐv mG_ #[39Îqs5WLٖ\ņ'&!tsӗoDqs˾?jUU1#Ɗj\j`jկZZm$""8,#a4^ј}{DV M]K]>Ar[f>X bxhS}>!5x'!"@Xt`у! ncq:0 %rs :IAj칔Vڬʎ+z㋵&2 T)}MCXArm8m !{ - ZBZ|q^Cd({=p &]m336䋕K^7eWY +ȐKح:.ئnW隈PcN룋XbsbLirG4 R 6qbD 8| KtJ}bNX('HhO_ IP܉Tj Z/6'͢;Z$PK_LR|z2@aIxd,Y&WRTSEHm)JLGuد]5XpP 3?HuA(<MPz:w'rS$32<rSִdyPi +b1z,{5#aI\V$5 FWb)%m/E 76"? +@Q7z+8_>3b͠C*6tACO]= hai]TܾjJyY#yJ 8T3/Q +}H)rVE406`=uj* u:Hp(So WwH@=HEO{U8)Vx"`ڇWlxbAK3 Y<n')Q粼y @ Yl8Z:,Ph|x"M\q4,zerָc D2dbȵ>$ >c)nD%vyj\hL\Fz )%8.7${K -tJb-ꐧGi{jB9_maed(0ˍ02P?U( +#zخ:2|y71U Q<[ϞzUNm1~2U+OփbhhIV @]ڈdlFb7MDOzIz4#)8XM1GjUۀ&$L"Q9lC0llR 'C.PhOUE^JX(dK cŬI\1I:b +ET0kAҊR㽂/j.dLdG5j%+d5Odr`̥CRG/Oc5}j/y]8e (*'thux#rQO𴓬[˚ѲX8B2 +R$T=9F |@O!.eԂP͞D} XnEQ<1iÆutp &I"z+kd:cLkšH$quiQmC|y2aa1=Dth1Ohб6u94Yhl4B_o_sM TnQdgo././̈r0tV, &%v ex34KEYx@ëAvSROJ$si%Չc[챯*W  +|jq(_.uE|vS]fpZuU@8,{\1a~IQ?!~Ơ%Q&37()l:L1}~ZG(*q2|WC:k L𮕡0wvgtpikKt=p_CgΊ_\Y| GVV \X:W++ {Ԁ,C'7<1l?x% W/_ (H??&,@p>'[%b74x8>013@|qGș|pg{*9%p"F/ @/(> !njHq30l&޳1c}:ȮŬ~@>ow6 .*.Q뎞+ ITwrMxlelgRm(A3_82&<{v2ɲ\ψL!oܠZ# \xN +n%h=WQ&GMzhoWI)pDbd Ƅ6:,ųٙIAnsmW9\{9W5s,˖κu΄!|f;$7P mߐ%vwn{/s.@BC`yu"1\.dU r#20VVxJ5#4!~몯u(ņ< ͕`E sI'9[>'*`:_t6 E0[vK@%_V*;qZCw(pGT Jr.״/t|0wm\.JU q cGg ծRq},nV6 w*}&E(4Gq,vVE=cmQw]7ȭx,ZMv6v[{!~8Z*>!Y⟼I>=\˻δ7HOMdQ?5xE~z0X2`5{I^ # +8J-i|wB(77@ fzk}P +(G&CE+jPO‘$'3Cś$K'Cy1v_Nޣg / u ܵ@{\m~B#E9&F}:g"8!mgDO;7ǢR}ڹ['N1(_bs]X +ʎaKy!>/䞬{z,"S״2EͿȲɐi'9"z5GZeEtR% +C]{S&:˷OEj"(F.Q5b?+\6mW1Wަ]ژE5tBexA=".SL(+VYVǏYKto[w[,n52Y?^A;пTϲf~<GxNQÄl%d1Go{Z8-Q?yWsQ?hX;@, +|*f\ܑ~Uػrq˺:,w`RY{bw;6D=R|kb=1VStoE)s#Y~"k3Q'!kk8MOr!MUؖT8qUlON KVYb*1մl +[>Kq`GN\&ƦW40>xE㇫w[/no÷g/?~{{g>}}f?]k]##C1)Vk|sZ +endstream +endobj + +786 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + >> + /Font << + /f0 704 0 R + /f1 728 0 R + /f2 674 0 R + /f3 722 0 R + /f4 710 0 R + /f5 692 0 R + /f6 656 0 R + /f7 698 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 24 + /Parent 1 0 R + /Contents 787 0 R +>> +endobj + +787 0 obj +<< + /Length 2987 + /Filter /FlateDecode +>> +stream +x\[s}ئM%1t nTi4c%m:*C}K.oK)zᒴ=8|o^wϟO?_}s~W/~z՝Wwnoo't>gB:tϨc+Ɖ1w/L_{/o' ktD͏䛏-@+jWWw';ً NjD.hߓ'f?ܮjy ۨ5Ĭ#W$`9 EB$p ׼̮`X&, b(O L^L=_u*^w&(yF'KDO?)09NX7\.Hl̮w ij}fra"~|^>g~^y[e&øws2A] ڨpJ6EkԉNkxbnk7Pek'T.Bzt=Rr~Wp۪Ⱥ#quaiG9uͰǵMS[$ ⭱ޫ-v˞.Z_>`WL WտӾZ}&'ͨƴȃCFTGe0Y7jrW 3_JO]~F?UG銣`Ƨ0QQ5ъ'0L5̲&1V}xtFR + ӏ&CPbl(8Jp|h0zHtbb1Aq\d,TU7V!*&*bgNrmgFצ jR0l?Zt)|) $j.Ҧ nΉUh!&Jö/v_!z]!j*PEl"v;1ː4iiW"!1=V[\%炉D (ʀ"~Lh;UsT&ȫQ"qq }l^Tnp.13.Ba [|pn+femI?9qYw +lZ!MSOOo|BbK?{\hM;Nii8Ϫra$MK3HIY2&)ۖ*RY956xQ9KH{*\(6m1J%6/<%ת ۵NBΎ:*vӂtNtTˤ5X;ʑ-;rU%)ueFq41'[iXt < cQV:@*J@qcs fWbNosR68v4MXmcsf\7e^aaWaũx;LbGӿ[U $NBHUfpiY6[~{:`GmWI#UI+p$8C+p$XWqJ&;+nٵ&U֟ ]kgw8Ⲏx(IӀVӵT!6~ ־N|D6xfSG~IpuKT<~Ɣy2$\ϴVAhpQ8 \m]r_4f%4FGLW=ZcmqhjBi'.Ww[N3XS$k]rU?) .dsUr;˨>ۙ{5³:]YyN(NDauB `qKY]$htv5v0oHM=#+R=PaJwb RPvYڜOmLǣWWR28$.IxA؄?Rd䚳Ǜ78EPUO|@˧v #-PoZְ^7 RT){w;۔s){27)0rP}%풺^\Q/Om*Cyj}4BΟ]+v5ӷa Zk;>]vczEy$zZ>k"3Gkt|R7OYUr\.ф3mD4ĭ!j6iei/Zj=+irPc8-{J7\85Nx  P.=UEUEPҎũ3;Uxg)ӷo>hmG69t6D{{مkc +endstream +endobj + +788 0 obj +<< + /Type /Page + /Resources << + /ProcSet [/PDF /Text /ImageC /ImageB] + /ColorSpace << + /c0 734 0 R + >> + /Font << + /f0 704 0 R + /f1 728 0 R + /f2 674 0 R + /f3 722 0 R + /f4 710 0 R + /f5 656 0 R + >> + >> + /MediaBox [0 0 595.2756 841.8898] + /StructParents 25 + /Parent 1 0 R + /Contents 789 0 R +>> +endobj + +789 0 obj +<< + /Length 1899 + /Filter /FlateDecode +>> +stream +xZێ}WHX{,vv$XDA+"9 rqUy]}~x-on}x_<}y+~}*6~鱸-)~.DJZ9CUp/_zS|-?$*7\ 7MOzzi~x{6ūCdGGKXd]{aʗ4\ DxUt/d͏mpT,Jr $J  dfi- 9h'h. tIP6 =PuP:=4SU4 дc#Nm !r2%Ē 9+6_ ßa1),)fEi8rhl +'B/.d.wlOqZm÷1v~lapܯ Pp|mOgٌ雔~2=<$n>?fu7 c<6HoO4ƹnlӫ6UM,KƸF#1kFlW^rRh`"s|hO1V +^i+Pα6N AF$L +#8EAQ&˚.ƨb +EowJ@ðZи +nac~*GgחzLQ`ɞ˝ٔcJWM kubtsp: Y)Ǒxr;)ęSᓣSY: $C,eSS.o!NM2lJ 2TPԑ[ǐD'ѪHk-0x4ZIk8#d|<(L˃Qpj0:mGpHX\e!b VQp+dʲ1O"YOGzkTjmk[&ҠX S4X'QEYޤA6:_&wrm홽5J9qc bvhvLF*K/3JkZ}HGx@,*SVur1JVXHƎ']nt$(ʓNzmߖVG#dqonx̏vSI$N@zA]5#g@Zyi-Hk #u#Ѻ]?*ۧOO+,s(-+څewE?T +endstream +endobj + +790 0 obj +<< + /Title + /Author + /Creator (Typst 0.14.0) + /ModDate (D:20251202011956+08'00) + /CreationDate (D:20251202011956+08'00) +>> +endobj + +791 0 obj +<< + /Length 1157 + /Type /Metadata + /Subtype /XML +>> +stream +经典搜索程景愉Typst 0.14.0en2025-12-02T01:19:56+08:002025-12-02T01:19:56+08:0012application/pdfPfHyFkN9zllPIAbcFPfXUg==X/wb7as1jn1ayguxI0s4yQ==proof1.7 +endstream +endobj + +792 0 obj +<< + /Type /Catalog + /Pages 1 0 R + /Metadata 791 0 R + /PageLabels 17 0 R + /Lang (en) + /StructTreeRoot 18 0 R + /MarkInfo << + /Marked true + /Suspects false + >> + /ViewerPreferences << + /Direction /L2R + >> + /Outlines 2 0 R +>> +endobj + +xref +0 793 +0000000000 65535 f +0000000016 00000 n +0000000171 00000 n +0000000252 00000 n +0000000360 00000 n +0000000482 00000 n +0000000604 00000 n +0000000781 00000 n +0000000945 00000 n +0000001123 00000 n +0000001302 00000 n +0000001454 00000 n +0000001675 00000 n +0000001908 00000 n +0000002129 00000 n +0000002359 00000 n +0000002483 00000 n +0000002593 00000 n +0000002748 00000 n +0000003233 00000 n +0000003348 00000 n +0000004015 00000 n +0000004618 00000 n +0000005925 00000 n +0000008144 00000 n +0000010979 00000 n +0000014134 00000 n +0000017489 00000 n +0000020004 00000 n +0000023055 00000 n +0000024150 00000 n +0000024505 00000 n +0000025193 00000 n +0000025292 00000 n +0000025451 00000 n +0000025575 00000 n +0000025681 00000 n +0000025769 00000 n +0000026013 00000 n +0000026145 00000 n +0000026230 00000 n +0000026322 00000 n +0000026412 00000 n +0000026497 00000 n +0000026578 00000 n +0000026655 00000 n +0000026752 00000 n +0000026842 00000 n +0000026981 00000 n +0000027066 00000 n +0000027204 00000 n +0000027297 00000 n +0000027388 00000 n +0000027478 00000 n +0000027563 00000 n +0000027659 00000 n +0000027749 00000 n +0000027834 00000 n +0000027915 00000 n +0000027992 00000 n +0000028089 00000 n +0000028179 00000 n +0000028318 00000 n +0000028403 00000 n +0000028545 00000 n +0000028638 00000 n +0000028729 00000 n +0000028819 00000 n +0000028904 00000 n +0000028996 00000 n +0000029086 00000 n +0000029171 00000 n +0000029252 00000 n +0000029329 00000 n +0000029426 00000 n +0000029516 00000 n +0000029655 00000 n +0000029740 00000 n +0000029862 00000 n +0000029952 00000 n +0000030041 00000 n +0000030126 00000 n +0000030217 00000 n +0000030306 00000 n +0000030391 00000 n +0000030472 00000 n +0000030549 00000 n +0000030644 00000 n +0000030733 00000 n +0000030872 00000 n +0000030957 00000 n +0000031094 00000 n +0000031184 00000 n +0000031273 00000 n +0000031358 00000 n +0000031449 00000 n +0000031538 00000 n +0000031624 00000 n +0000031705 00000 n +0000031782 00000 n +0000031877 00000 n +0000031967 00000 n +0000032109 00000 n +0000032198 00000 n +0000032313 00000 n +0000032405 00000 n +0000032497 00000 n +0000032588 00000 n +0000032677 00000 n +0000032770 00000 n +0000032861 00000 n +0000032949 00000 n +0000033033 00000 n +0000033113 00000 n +0000033210 00000 n +0000033301 00000 n +0000033443 00000 n +0000033532 00000 n +0000033654 00000 n +0000033746 00000 n +0000033837 00000 n +0000033926 00000 n +0000034019 00000 n +0000034110 00000 n +0000034198 00000 n +0000034282 00000 n +0000034362 00000 n +0000034459 00000 n +0000034550 00000 n +0000034692 00000 n +0000034781 00000 n +0000034903 00000 n +0000034995 00000 n +0000035086 00000 n +0000035175 00000 n +0000035268 00000 n +0000035359 00000 n +0000035447 00000 n +0000035531 00000 n +0000035611 00000 n +0000035708 00000 n +0000035799 00000 n +0000035887 00000 n +0000036109 00000 n +0000036198 00000 n +0000036287 00000 n +0000036376 00000 n +0000036465 00000 n +0000036554 00000 n +0000036643 00000 n +0000036732 00000 n +0000036821 00000 n +0000036910 00000 n +0000036999 00000 n +0000037088 00000 n +0000037177 00000 n +0000037292 00000 n +0000037386 00000 n +0000037494 00000 n +0000037584 00000 n +0000037726 00000 n +0000037884 00000 n +0000038002 00000 n +0000038099 00000 n +0000038194 00000 n +0000038408 00000 n +0000038510 00000 n +0000038640 00000 n +0000038758 00000 n +0000038880 00000 n +0000039026 00000 n +0000039136 00000 n +0000039209 00000 n +0000039343 00000 n +0000039461 00000 n +0000039619 00000 n +0000039729 00000 n +0000039838 00000 n +0000039931 00000 n +0000040026 00000 n +0000040179 00000 n +0000040272 00000 n +0000040365 00000 n +0000040460 00000 n +0000040680 00000 n +0000040786 00000 n +0000040928 00000 n +0000041066 00000 n +0000041184 00000 n +0000041281 00000 n +0000041376 00000 n +0000041678 00000 n +0000041816 00000 n +0000041906 00000 n +0000042028 00000 n +0000042118 00000 n +0000042252 00000 n +0000042418 00000 n +0000042592 00000 n +0000042738 00000 n +0000042872 00000 n +0000042979 00000 n +0000043068 00000 n +0000043190 00000 n +0000043318 00000 n +0000043431 00000 n +0000043535 00000 n +0000043624 00000 n +0000043722 00000 n +0000043829 00000 n +0000043918 00000 n +0000044034 00000 n +0000044144 00000 n +0000044292 00000 n +0000044401 00000 n +0000044494 00000 n +0000044589 00000 n +0000044766 00000 n +0000044861 00000 n +0000045057 00000 n +0000045163 00000 n +0000045305 00000 n +0000045463 00000 n +0000045581 00000 n +0000045678 00000 n +0000045773 00000 n +0000046115 00000 n +0000046241 00000 n +0000046331 00000 n +0000046465 00000 n +0000046631 00000 n +0000046805 00000 n +0000046951 00000 n +0000047061 00000 n +0000047151 00000 n +0000047285 00000 n +0000047427 00000 n +0000047549 00000 n +0000047659 00000 n +0000047749 00000 n +0000047851 00000 n +0000047965 00000 n +0000048055 00000 n +0000048169 00000 n +0000048290 00000 n +0000048421 00000 n +0000048528 00000 n +0000048617 00000 n +0000048715 00000 n +0000048825 00000 n +0000048914 00000 n +0000049024 00000 n +0000049134 00000 n +0000049268 00000 n +0000049375 00000 n +0000049467 00000 n +0000049561 00000 n +0000049701 00000 n +0000049794 00000 n +0000050002 00000 n +0000050098 00000 n +0000050240 00000 n +0000050398 00000 n +0000050516 00000 n +0000050613 00000 n +0000050708 00000 n +0000051106 00000 n +0000051208 00000 n +0000051326 00000 n +0000051399 00000 n +0000051545 00000 n +0000051687 00000 n +0000051777 00000 n +0000051899 00000 n +0000052057 00000 n +0000052211 00000 n +0000052301 00000 n +0000052423 00000 n +0000052553 00000 n +0000052699 00000 n +0000052789 00000 n +0000052971 00000 n +0000053109 00000 n +0000053227 00000 n +0000053425 00000 n +0000053515 00000 n +0000053633 00000 n +0000053747 00000 n +0000053885 00000 n +0000054007 00000 n +0000054080 00000 n +0000054194 00000 n +0000054312 00000 n +0000054450 00000 n +0000054564 00000 n +0000054637 00000 n +0000054767 00000 n +0000054943 00000 n +0000055050 00000 n +0000055148 00000 n +0000055267 00000 n +0000055374 00000 n +0000055466 00000 n +0000055560 00000 n +0000055730 00000 n +0000055822 00000 n +0000055914 00000 n +0000056006 00000 n +0000056098 00000 n +0000056192 00000 n +0000056388 00000 n +0000056489 00000 n +0000056631 00000 n +0000056784 00000 n +0000056886 00000 n +0000056983 00000 n +0000057078 00000 n +0000057428 00000 n +0000057534 00000 n +0000057624 00000 n +0000057782 00000 n +0000057916 00000 n +0000058042 00000 n +0000058176 00000 n +0000058302 00000 n +0000058448 00000 n +0000058538 00000 n +0000058668 00000 n +0000058758 00000 n +0000058860 00000 n +0000058986 00000 n +0000059076 00000 n +0000059198 00000 n +0000059288 00000 n +0000059382 00000 n +0000059548 00000 n +0000059638 00000 n +0000059780 00000 n +0000059910 00000 n +0000060000 00000 n +0000060162 00000 n +0000060296 00000 n +0000060422 00000 n +0000060536 00000 n +0000060662 00000 n +0000060808 00000 n +0000060917 00000 n +0000061010 00000 n +0000061105 00000 n +0000061266 00000 n +0000061414 00000 n +0000061522 00000 n +0000061630 00000 n +0000061725 00000 n +0000061853 00000 n +0000061951 00000 n +0000062093 00000 n +0000062239 00000 n +0000062349 00000 n +0000062445 00000 n +0000062539 00000 n +0000062865 00000 n +0000062966 00000 n +0000063055 00000 n +0000063195 00000 n +0000063317 00000 n +0000063433 00000 n +0000063564 00000 n +0000063653 00000 n +0000063767 00000 n +0000063855 00000 n +0000063949 00000 n +0000064075 00000 n +0000064165 00000 n +0000064287 00000 n +0000064377 00000 n +0000064471 00000 n +0000064637 00000 n +0000064727 00000 n +0000064869 00000 n +0000064999 00000 n +0000065089 00000 n +0000065251 00000 n +0000065377 00000 n +0000065491 00000 n +0000065617 00000 n +0000065743 00000 n +0000065852 00000 n +0000065945 00000 n +0000066040 00000 n +0000066169 00000 n +0000066264 00000 n +0000066420 00000 n +0000066526 00000 n +0000066668 00000 n +0000066826 00000 n +0000066944 00000 n +0000067041 00000 n +0000067136 00000 n +0000067462 00000 n +0000067568 00000 n +0000067658 00000 n +0000067792 00000 n +0000067926 00000 n +0000068056 00000 n +0000068202 00000 n +0000068292 00000 n +0000068422 00000 n +0000068512 00000 n +0000068614 00000 n +0000068736 00000 n +0000068825 00000 n +0000068932 00000 n +0000069021 00000 n +0000069113 00000 n +0000069226 00000 n +0000069315 00000 n +0000069437 00000 n +0000069556 00000 n +0000069645 00000 n +0000069770 00000 n +0000069886 00000 n +0000069996 00000 n +0000070112 00000 n +0000070218 00000 n +0000070327 00000 n +0000070420 00000 n +0000070515 00000 n +0000070676 00000 n +0000070771 00000 n +0000070927 00000 n +0000071033 00000 n +0000071175 00000 n +0000071333 00000 n +0000071451 00000 n +0000071548 00000 n +0000071643 00000 n +0000071969 00000 n +0000072075 00000 n +0000072165 00000 n +0000072299 00000 n +0000072433 00000 n +0000072563 00000 n +0000072709 00000 n +0000072799 00000 n +0000072929 00000 n +0000073019 00000 n +0000073121 00000 n +0000073247 00000 n +0000073337 00000 n +0000073451 00000 n +0000073541 00000 n +0000073635 00000 n +0000073757 00000 n +0000073847 00000 n +0000073981 00000 n +0000074104 00000 n +0000074193 00000 n +0000074318 00000 n +0000074434 00000 n +0000074544 00000 n +0000074660 00000 n +0000074776 00000 n +0000074883 00000 n +0000074975 00000 n +0000075069 00000 n +0000075216 00000 n +0000075310 00000 n +0000075466 00000 n +0000075567 00000 n +0000075687 00000 n +0000075779 00000 n +0000075946 00000 n +0000076035 00000 n +0000076164 00000 n +0000076255 00000 n +0000076346 00000 n +0000076439 00000 n +0000076529 00000 n +0000076618 00000 n +0000076788 00000 n +0000076881 00000 n +0000076974 00000 n +0000077069 00000 n +0000077161 00000 n +0000077250 00000 n +0000077388 00000 n +0000077481 00000 n +0000077574 00000 n +0000077669 00000 n +0000077761 00000 n +0000077850 00000 n +0000078028 00000 n +0000078121 00000 n +0000078214 00000 n +0000078307 00000 n +0000078400 00000 n +0000078495 00000 n +0000078587 00000 n +0000078676 00000 n +0000078873 00000 n +0000078966 00000 n +0000079059 00000 n +0000079152 00000 n +0000079245 00000 n +0000079337 00000 n +0000079431 00000 n +0000079522 00000 n +0000079647 00000 n +0000079739 00000 n +0000079831 00000 n +0000079939 00000 n +0000080031 00000 n +0000080222 00000 n +0000080311 00000 n +0000080415 00000 n +0000080512 00000 n +0000080603 00000 n +0000080692 00000 n +0000080808 00000 n +0000080905 00000 n +0000080996 00000 n +0000081085 00000 n +0000081195 00000 n +0000081292 00000 n +0000081383 00000 n +0000081472 00000 n +0000081576 00000 n +0000081673 00000 n +0000081764 00000 n +0000081853 00000 n +0000081963 00000 n +0000082060 00000 n +0000082151 00000 n +0000082240 00000 n +0000082344 00000 n +0000082447 00000 n +0000082538 00000 n +0000082627 00000 n +0000082731 00000 n +0000082834 00000 n +0000082925 00000 n +0000083014 00000 n +0000083115 00000 n +0000083218 00000 n +0000083309 00000 n +0000083403 00000 n +0000083511 00000 n +0000083603 00000 n +0000083740 00000 n +0000083848 00000 n +0000083938 00000 n +0000084067 00000 n +0000084150 00000 n +0000084290 00000 n +0000084453 00000 n +0000084544 00000 n +0000084627 00000 n +0000084767 00000 n +0000084930 00000 n +0000085021 00000 n +0000085159 00000 n +0000085242 00000 n +0000085382 00000 n +0000085551 00000 n +0000085642 00000 n +0000085725 00000 n +0000085865 00000 n +0000086034 00000 n +0000086125 00000 n +0000086208 00000 n +0000086348 00000 n +0000086517 00000 n +0000086608 00000 n +0000086691 00000 n +0000086831 00000 n +0000087000 00000 n +0000087091 00000 n +0000087174 00000 n +0000087314 00000 n +0000087480 00000 n +0000087571 00000 n +0000087654 00000 n +0000087794 00000 n +0000087963 00000 n +0000088054 00000 n +0000088137 00000 n +0000088277 00000 n +0000088446 00000 n +0000088537 00000 n +0000088620 00000 n +0000088760 00000 n +0000088929 00000 n +0000089020 00000 n +0000089103 00000 n +0000089243 00000 n +0000089406 00000 n +0000089497 00000 n +0000089580 00000 n +0000089720 00000 n +0000089883 00000 n +0000089974 00000 n +0000090057 00000 n +0000090197 00000 n +0000090357 00000 n +0000090447 00000 n +0000090530 00000 n +0000090670 00000 n +0000090830 00000 n +0000090920 00000 n +0000091020 00000 n +0000091110 00000 n +0000091210 00000 n +0000091421 00000 n +0000091594 00000 n +0000091681 00000 n +0000091823 00000 n +0000091911 00000 n +0000092080 00000 n +0000092155 00000 n +0000092230 00000 n +0000092312 00000 n +0000092456 00000 n +0000092538 00000 n +0000092681 00000 n +0000092756 00000 n +0000092831 00000 n +0000092913 00000 n +0000093056 00000 n +0000093138 00000 n +0000093285 00000 n +0000093360 00000 n +0000093435 00000 n +0000093517 00000 n +0000093660 00000 n +0000093742 00000 n +0000093885 00000 n +0000093990 00000 n +0000094065 00000 n +0000094140 00000 n +0000094222 00000 n +0000094365 00000 n +0000094447 00000 n +0000094590 00000 n +0000094732 00000 n +0000094775 00000 n +0000094834 00000 n +0000094893 00000 n +0000094952 00000 n +0000095011 00000 n +0000095070 00000 n +0000095129 00000 n +0000095188 00000 n +0000095247 00000 n +0000095307 00000 n +0000095367 00000 n +0000095427 00000 n +0000095612 00000 n +0000096332 00000 n +0000096423 00000 n +0000096678 00000 n +0000098122 00000 n +0000103750 00000 n +0000103931 00000 n +0000104174 00000 n +0000104260 00000 n +0000104513 00000 n +0000105269 00000 n +0000107118 00000 n +0000107273 00000 n +0000107527 00000 n +0000107617 00000 n +0000107874 00000 n +0000109346 00000 n +0000119886 00000 n +0000120066 00000 n +0000120309 00000 n +0000120398 00000 n +0000120649 00000 n +0000121658 00000 n +0000126395 00000 n +0000126578 00000 n +0000126824 00000 n +0000126913 00000 n +0000127166 00000 n +0000128175 00000 n +0000132788 00000 n +0000132964 00000 n +0000133257 00000 n +0000133345 00000 n +0000133591 00000 n +0000134401 00000 n +0000135931 00000 n +0000136096 00000 n +0000136364 00000 n +0000136453 00000 n +0000136730 00000 n +0000138412 00000 n +0000150290 00000 n +0000150472 00000 n +0000150877 00000 n +0000150966 00000 n +0000151218 00000 n +0000152213 00000 n +0000154998 00000 n +0000155154 00000 n +0000155410 00000 n +0000155501 00000 n +0000155772 00000 n +0000161074 00000 n +0000215531 00000 n +0000215699 00000 n +0000216450 00000 n +0000216539 00000 n +0000216829 00000 n +0000218161 00000 n +0000248427 00000 n +0000248607 00000 n +0000249166 00000 n +0000249257 00000 n +0000249518 00000 n +0000250752 00000 n +0000255689 00000 n +0000255873 00000 n +0000256121 00000 n +0000256212 00000 n +0000256466 00000 n +0000261572 00000 n +0000332360 00000 n +0000332533 00000 n +0000332855 00000 n +0000332943 00000 n +0000333237 00000 n +0000334049 00000 n +0000344853 00000 n +0000344891 00000 n +0000344929 00000 n +0000345288 00000 n +0000345711 00000 n +0000345764 00000 n +0000345817 00000 n +0000345871 00000 n +0000345924 00000 n +0000345978 00000 n +0000346031 00000 n +0000346085 00000 n +0000346138 00000 n +0000346191 00000 n +0000346244 00000 n +0000346297 00000 n +0000346350 00000 n +0000346403 00000 n +0000346456 00000 n +0000346802 00000 n +0000347340 00000 n +0000347668 00000 n +0000349329 00000 n +0000349569 00000 n +0000349811 00000 n +0000350051 00000 n +0000350305 00000 n +0000350601 00000 n +0000350897 00000 n +0000351195 00000 n +0000351463 00000 n +0000351802 00000 n +0000352151 00000 n +0000352488 00000 n +0000352855 00000 n +0000353100 00000 n +0000353347 00000 n +0000353811 00000 n +0000361591 00000 n +0000361992 00000 n +0000366337 00000 n +0000366720 00000 n +0000371284 00000 n +0000371631 00000 n +0000376512 00000 n +0000376877 00000 n +0000382193 00000 n +0000382558 00000 n +0000388427 00000 n +0000388774 00000 n +0000393895 00000 n +0000394260 00000 n +0000399588 00000 n +0000399989 00000 n +0000403056 00000 n +0000403421 00000 n +0000405400 00000 n +0000405588 00000 n +0000406836 00000 n +trailer +<< + /Size 793 + /Root 792 0 R + /Info 790 0 R + /ID [(X/wb7as1jn1ayguxI0s4yQ==) (PfHyFkN9zllPIAbcFPfXUg==)] +>> +startxref +407095 +%%EOF \ No newline at end of file diff --git a/proj1/main.typ b/proj1/main.typ new file mode 100755 index 0000000..c27882b --- /dev/null +++ b/proj1/main.typ @@ -0,0 +1,430 @@ +#import "labtemplate.typ": * +#show: nudtlabpaper.with( + author: "程景愉", + id: "202302723005", + title: "经典搜索", + training_type: "普通本科生", + grade: "2023级", + major: "网络工程", + department: "计算机学院", + advisor: "胡罡", + jobtitle: "教授", + lab: "306-707", + date: "2025.12.02", + header_str: "经典搜索实验报告", + simple_cover: true, +) + +#set page(header: [ + #set par(spacing: 6pt) + #align(center)[#text(size: 11pt)[人工智能实验报告]] + #v(-0.3em) + #line(length: 100%, stroke: (thickness: 1pt)) +],) + +#show heading: it => if it.level == 1 [ + #v(1em) + #set text(font: hei, size: 16pt) + #counter(heading).display() + #h(0.5em) + #it.body + #v(0.5em) +] else [ + #v(0.8em) + #set text(font: "New Computer Modern", weight: "bold", style: "italic") + #counter(heading).display(it.numbering) + #h(0.5em) + #it.body + #v(0.5em) +] + +#outline(title: "目录",depth: 2, indent: 1em) + +#set enum(indent: 0.5em,body-indent: 0.5em,) +#pagebreak() + += 实验介绍 +#para[ +本项目是UC Berkeley CS188人工智能课程的第一个项目,主要任务是实现多种通用搜索算法,并将其应用于解决经典游戏吃豆人(Pacman)中的路径规划问题。项目涵盖了从基础的无信息搜索算法(如DFS、BFS)到有信息搜索算法(如UCS、A\*),以及针对特定问题(如访问所有角落、吃掉所有食物)的状态表示和启发式函数设计。通过本项目,我们将深入理解搜索算法的原理,并学会如何利用它们来解决实际问题。 +] + += 实验内容 +#para[ +本次实验内容涵盖了8个递进的编程任务,具体如下: +] ++ *Q1: 深度优先搜索 (DFS)*:利用深度优先搜索算法寻找迷宫中的固定食物点。 ++ *Q2: 广度优先搜索 (BFS)*:利用广度优先搜索算法,找到到达目标所需行动次数最少的路径。 ++ *Q3: 一致代价搜索 (UCS)*:实现一致代价搜索,以处理具有不同行动代价的路径,找到总代价最小的路径。 ++ *Q4: A\* 搜索*:实现A\*搜索算法,该算法结合了路径的实际代价和启发式评估,并使用曼哈顿距离作为启发式函数进行测试。 ++ *Q5: 角落问题*:设计一个新的搜索问题,要求吃豆人以最短路径访问迷宫的全部四个角落。这需要定义一个合适的状态空间。 ++ *Q6: 角落启发式*:为角落问题设计一个一致且可接受的启发式函数,以加速A\*搜索过程。 ++ *Q7: 食物启发式*:为“吃完所有食物”这一复杂问题设计一个高效的启发式函数,以在可接受的时间内找到优良路径。 ++ *Q8: 寻找最近点路径*:实现一个次优的贪心策略,让吃豆人总是前往最近的食物点,作为一种快速解决复杂问题的近似方法。 + += 实验要求 +#para[ +本项目要求在 `search.py` 和 `searchAgents.py` 两个文件中根据指引补全代码,核心要求如下: +] ++ *通用搜索算法*:在 `search.py` 中实现 `depthFirstSearch`, `breadthFirstSearch`, `uniformCostSearch`, 和 `aStarSearch` 四个核心搜索算法。所有搜索函数都需要返回一个动作(action)列表,该列表能够引导吃豆人从起点到达目标。 ++ *数据结构*:必须使用项目框架中 `util.py` 提供的 `Stack`, `Queue` 和 `PriorityQueue` 数据结构,以确保与自动评分器的兼容性。 ++ *问题建模*:在 `searchAgents.py` 中,需要为角落问题(`CornersProblem`)选择并实现一个不包含冗余信息的高效状态表示。 ++ *启发式函数设计*:为角落问题和食物问题设计的启发式函数(`cornersHeuristic` 和 `foodHeuristic`)必须是无不足道的(non-trivial)、非负且一致的(consistent)。启发式函数的性能将根据其在A\*搜索中扩展的节点数量进行评分。 ++ *目标测试*:为 `AnyFoodSearchProblem` 补全目标测试函数 `isGoalState`,使其能够正确判断是否到达任意一个食物所在的位置。 + += 实验步骤与实现 +== Q1: 深度优先搜索 (DFS) +*实现思路*:深度优先搜索(DFS)优先探索最深的节点。我们使用图搜索版本,即记录已访问的节点以避免冗余搜索和无限循环。数据结构上,DFS采用栈(Stack)来实现“后进先出”(LIFO)的节点扩展顺序。 + +*核心代码* (`search.py`): +```python +def depthFirstSearch(problem: SearchProblem): + fringe = util.Stack() + visited = set() + startState = problem.getStartState() + fringe.push((startState, [])) + + while not fringe.isEmpty(): + currentState, actions = fringe.pop() + + if currentState in visited: + continue + + visited.add(currentState) + + if problem.isGoalState(currentState): + return actions + + successors = problem.getSuccessors(currentState) + + for successor, action, cost in successors: + if successor not in visited: + newActions = actions + [action] + fringe.push((successor, newActions)) + + return [] +``` + +*测试指令*: +```fish +python autograder.py -q q1 +python pacman.py -l mediumMaze -p SearchAgent -a fn=dfs +``` + +== Q2: 广度优先搜索 (BFS) +*实现思路*:广度优先搜索(BFS)逐层扩展节点,确保找到行动次数最少的路径。同样采用图搜索版本。数据结构上,BFS采用队列(Queue)来实现“先进先出”(FIFO)的节点扩展顺序。 + +*核心代码* (`search.py`): +```python +def breadthFirstSearch(problem: SearchProblem): + fringe = util.Queue() + visited = set() + startState = problem.getStartState() + fringe.push((startState, [])) + + while not fringe.isEmpty(): + currentState, actions = fringe.pop() + + if currentState in visited: + continue + + visited.add(currentState) + + if problem.isGoalState(currentState): + return actions + + successors = problem.getSuccessors(currentState) + + for successor, action, cost in successors: + if successor not in visited: + newActions = actions + [action] + fringe.push((successor, newActions)) + + return [] +``` + +*测试指令*: +```fish +python autograder.py -q q2 +python pacman.py -l mediumMaze -p SearchAgent -a fn=bfs +``` + +== Q3: 一致代价搜索 (UCS) +*实现思路*:一致代价搜索(UCS)扩展总路径代价最小的节点,从而保证找到最优(总代价最低)的路径。它通过使用优先队列(PriorityQueue)来实现,节点的优先级由其累积路径代价决定。 + +*核心代码* (`search.py`): +```python +def uniformCostSearch(problem: SearchProblem): + fringe = util.PriorityQueue() + visited = {} + startState = problem.getStartState() + fringe.push((startState, [], 0), 0) + + while not fringe.isEmpty(): + currentState, actions, currentCost = fringe.pop() + + if currentState in visited and currentCost >= visited[currentState]: + continue + + visited[currentState] = currentCost + + if problem.isGoalState(currentState): + return actions + + successors = problem.getSuccessors(currentState) + + for successor, action, stepCost in successors: + newCost = currentCost + stepCost + newActions = actions + [action] + fringe.push((successor, newActions, newCost), newCost) + + return [] +``` + +*测试指令*: +```fish +python autograder.py -q q3 +python pacman.py -l mediumMaze -p SearchAgent -a fn=ucs +``` + +== Q4: A\* 搜索 +*实现思路*:A\*搜索是UCS的扩展,它在评估节点时不仅考虑已付出的代价$g(n)$,还引入了对未来代价的估计,即启发式函数$h(n)$。节点优先级由$f(n) = g(n) + h(n)$决定。这使得A\*能够更智能地朝向目标进行搜索。 + +*核心代码* (`search.py`): +```python +def aStarSearch(problem: SearchProblem, heuristic=nullHeuristic): + fringe = util.PriorityQueue() + visited = {} + startState = problem.getStartState() + startHeuristic = heuristic(startState, problem) + fringe.push((startState, [], 0), startHeuristic) + + while not fringe.isEmpty(): + currentState, actions, currentCost = fringe.pop() + + if currentState in visited and currentCost >= visited[currentState]: + continue + + visited[currentState] = currentCost + + if problem.isGoalState(currentState): + return actions + + successors = problem.getSuccessors(currentState) + + for successor, action, stepCost in successors: + newCost = currentCost + stepCost + newHeuristic = heuristic(successor, problem) + fValue = newCost + newHeuristic + newActions = actions + [action] + fringe.push((successor, newActions, newCost), fValue) + + return [] +``` + +*测试指令*: +```fish +python autograder.py -q q4 +python pacman.py -l bigMaze -z .5 -p SearchAgent -a fn=astar,heuristic=manhattanHeuristic +``` + +== Q5: 角落问题 (Corners Problem) +*实现思路*:为了解决访问所有四个角落的问题,我们需要设计一个能够追踪Pacman位置和哪些角落已被访问的状态表示。一个合适的状态是 `(position, visited_corners)`,其中 `position` 是 `(x, y)` 坐标,`visited_corners` 是一个布尔元组,记录四个角落各自的访问情况。 + +*核心代码* (`searchAgents.py`): +```python +// 状态表示:(当前位置, 已访问的角落元组) +// getStartState +def getStartState(self): + cornersVisited = tuple([self.startingPosition == corner for corner in self.corners]) + return (self.startingPosition, cornersVisited) + +// isGoalState: 检查是否所有角落都已访问 +def isGoalState(self, state: Any): + _, cornersVisited = state + return all(cornersVisited) + +// getSuccessors: 生成后继状态,并更新角落访问信息 +def getSuccessors(self, state: Any): + successors = [] + currentPosition, cornersVisited = state + + for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: + x, y = currentPosition + dx, dy = Actions.directionToVector(action) + nextx, nexty = int(x + dx), int(y + dy) + + if not self.walls[nextx][nexty]: + nextPosition = (nextx, nexty) + newCornersVisited = list(cornersVisited) + + for i, corner in enumerate(self.corners): + if nextPosition == corner and not newCornersVisited[i]: + newCornersVisited[i] = True + + successorState = (nextPosition, tuple(newCornersVisited)) + successors.append((successorState, action, 1)) + + self._expanded += 1 + return successors +``` + +*测试指令*: +```fish +python autograder.py -q q5 +python pacman.py -l mediumCorners -p SearchAgent -a fn=bfs,prob=CornersProblem +``` + +== Q6: 角落启发式 (Corners Heuristic) +*实现思路*:为角落问题设计一个一致且可接受的启发式函数,关键是估计从当前状态到达目标状态(所有角落被访问)的最小代价。一个有效的策略是计算当前位置到所有未访问角落的“某种”距离。我们采用的策略是:启发式的值取“当前位置到最远的未访问角落的曼哈顿距离”和“所有未访问角落两两之间最远曼哈顿距离”中的较大者。这确保了启发式是可接受的,因为它低估了必须走过的总路程。 + +*核心代码* (`searchAgents.py`): +```python +def cornersHeuristic(state: Any, problem: CornersProblem): + currentPosition, cornersVisited = state + corners = problem.corners + + if all(cornersVisited): + return 0 + + unvisitedCorners = [] + for i, corner in enumerate(corners): + if not cornersVisited[i]: + unvisitedCorners.append(corner) + + if not unvisitedCorners: + return 0 + + maxDistance = 0 + for corner in unvisitedCorners: + distance = util.manhattanDistance(currentPosition, corner) + maxDistance = max(maxDistance, distance) + + maxCornerDistance = 0 + for i in range(len(unvisitedCorners)): + for j in range(i + 1, len(unvisitedCorners)): + distance = util.manhattanDistance(unvisitedCorners[i], unvisitedCorners[j]) + maxCornerDistance = max(maxCornerDistance, distance) + + return max(maxDistance, maxCornerDistance) +``` +*测试指令*: +```fish +python autograder.py -q q6 +python pacman.py -l mediumCorners -p AStarCornersAgent -z 0.5 +``` + +== Q7: 食物启发式 (Food Heuristic) +*实现思路*:为“吃掉所有食物”问题设计启发式函数更具挑战性。状态包含当前位置和食物分布的网格。一个好的启发式需要有效估计吃掉所有剩余食物的最小步数。我们结合了多种策略来构造一个更强的启发式:取“当前位置到最远食物的曼哈顿距离”、“剩余食物中两两之间最远的曼哈顿距离”以及“剩余食物的数量”这三者的最大值。这个值仍然是真实代价的下界,保证了可接受性和一致性。 + +*核心代码* (`searchAgents.py`): +```python +def foodHeuristic(state: Tuple[Tuple, List[List]], problem: FoodSearchProblem): + position, foodGrid = state + foodList = foodGrid.asList() + + if not foodList: + return 0 + + maxDistance = 0 + for food in foodList: + distance = util.manhattanDistance(position, food) + maxDistance = max(maxDistance, distance) + + maxFoodDistance = 0 + if len(foodList) > 1: + for i in range(len(foodList)): + for j in range(i + 1, len(foodList)): + distance = util.manhattanDistance(foodList[i], foodList[j]) + maxFoodDistance = max(maxFoodDistance, distance) + + foodCount = len(foodList) + + return max(maxDistance, maxFoodDistance, foodCount) +``` +*测试指令*: +```fish +python autograder.py -q q7 +python pacman.py -l trickySearch -p AStarFoodSearchAgent +``` + +== Q8: 寻找最近点路径 (Closest Dot Search) +*实现思路*:这是一个贪心算法,它不保证找到全局最优解,但通常能快速找到一个较好的解。策略是:重复寻找并移动到距离当前位置最近的食物点,直到所有食物被吃完。这个任务的核心是实现 `findPathToClosestDot` 函数。我们可以定义一个 `AnyFoodSearchProblem`,其目标是到达任意一个食物点,然后使用BFS来找到到达最近食物的最短路径。 + +*核心代码* (`searchAgents.py`): +```python +// AnyFoodSearchProblem 的目标测试 +def isGoalState(self, state: Tuple[int, int]): + x,y = state + return self.food[x][y] + +// findPathToClosestDot 函数实现 +def findPathToClosestDot(self, gameState: pacman.GameState): + problem = AnyFoodSearchProblem(gameState) + // BFS保证找到步数最少的路径,即到达最近的食物 + path = search.bfs(problem) + return path +``` +*测试指令*: +```fish +python autograder.py -q q8 +python pacman.py -l bigSearch -p ClosestDotSearchAgent -z .5 +``` + += 实验结果 +#para[ +本项目的所有8个任务均已成功实现,并通过了自动评分器(autograder)的所有测试用例,最终取得了 *25/25* 的满分成绩。自动评分器给出的最终成绩摘要如下: +] + +``` +Provisional grades +================== +Question q1: 3/3 +Question q2: 3/3 +Question q3: 3/3 +Question q4: 3/3 +Question q5: 3/3 +Question q6: 3/3 +Question q7: 4/4 +Question q8: 3/3 +------------------ +Total: 25/25 +``` + +#para[ +以下是各部分任务的详细测试结果摘要: +] ++ *Q1: 深度优先搜索* + - 所有测试用例通过。 + - 在 `mediumMaze` 迷宫中找到一条长度为130的路径,扩展了146个节点。 ++ *Q2: 广度优先搜索* + - 所有测试用例通过。 + - 在 `mediumMaze` 迷宫中找到长度为68的最短路径,扩展了269个节点。 ++ *Q3: 一致代价搜索* + - 所有测试用例通过。 + - 在不同代价的迷宫中(`mediumDottedMaze`, `mediumScaryMaze`)均能找到最优路径。 ++ *Q4: A\*搜索* + - 所有测试用例通过。 + - 在 `bigMaze` 中,A\* 搜索扩展的节点数(约549)少于UCS(约620),展现了启发式的有效性。 ++ *Q5: 角落问题* + - 所有测试用例通过。 + - 在 `mediumCorners` 上,BFS扩展了接近2000个节点。 ++ *Q6: 角落启发式* + - 所有测试用例通过,启发式满足一致性。 + - 在 `mediumCorners` 上,使用A\*和该启发式将扩展节点数减少到 *961* 个,满足了评分的最高要求。 ++ *Q7: 食物启发式* + - 18个测试用例全部通过,启发式满足一致性。 + - 在 `trickySearch` 上,扩展节点数为 *8763* 个,达到了满分(4/4)要求。 ++ *Q8: 寻找最近点路径* + - 所有测试用例通过,贪心策略能够成功吃掉所有食物。 + +#pagebreak() += 实验总结 +#para[ +通过本次实验,我成功实现了包括深度优先搜索(DFS)、广度优先搜索(BFS)、一致代价搜索(UCS)和A\*搜索在内的多种经典搜索算法,并将其应用于解决Pacman游戏中的路径规划问题。 +] +#para[ +在实验过程中,我遇到的主要挑战在于为复杂问题设计高效、一致且可接受的启发式函数,特别是Q6的角落问题和Q7的食物收集问题。通过对问题进行抽象建模,并反复迭代优化启发式策略,最终设计出的启发式函数在满足一致性的前提下,显著减少了搜索节点的扩展数量,达到了评分要求。例如,在Q7中,通过比较“到最远食物的距离”、“食物间的最大距离”和“食物数量”等多个策略,并取其最大值作为启发式,最终将扩展节点数控制在了9000以内。 +] +#para[ +通过这个项目,我不仅深入掌握了各种搜索算法的原理、实现细节及其适用场景,还深刻理解了状态空间表示、启发式函数设计(可接受性与一致性)对搜索性能的决定性影响。这些知识和经验对于解决更广泛的人工智能规划问题具有重要的实践意义。未来,可以尝试引入更高级的启发式策略(如基于最小生成树的启发式)或探索其他搜索算法变体,以期进一步提升求解效率。 +] diff --git a/proj1/pacman.py b/proj1/pacman.py new file mode 100755 index 0000000..0a527c2 --- /dev/null +++ b/proj1/pacman.py @@ -0,0 +1,684 @@ +# pacman.py +# --------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +""" +Pacman.py holds the logic for the classic pacman game along with the main +code to run a game. This file is divided into three sections: + + (i) Your interface to the pacman world: + Pacman is a complex environment. You probably don't want to + read through all of the code we wrote to make the game runs + correctly. This section contains the parts of the code + that you will need to understand in order to complete the + project. There is also some code in game.py that you should + understand. + + (ii) The hidden secrets of pacman: + This section contains all of the logic code that the pacman + environment uses to decide who can move where, who dies when + things collide, etc. You shouldn't need to read this section + of code, but you can if you want. + + (iii) Framework to start a game: + The final section contains the code for reading the command + you use to set up the game, then starting up a new game, along with + linking in all the external parts (agent functions, graphics). + Check this section out to see all the options available to you. + +To play your first game, type 'python pacman.py' from the command line. +The keys are 'a', 's', 'd', and 'w' to move (or arrow keys). Have fun! +""" +from game import GameStateData +from game import Game +from game import Directions +from game import Actions +from util import nearestPoint +from util import manhattanDistance +import util, layout +import sys, types, time, random, os + +################################################### +# YOUR INTERFACE TO THE PACMAN WORLD: A GameState # +################################################### + +class GameState: + """ + A GameState specifies the full game state, including the food, capsules, + agent configurations and score changes. + + GameStates are used by the Game object to capture the actual state of the game and + can be used by agents to reason about the game. + + Much of the information in a GameState is stored in a GameStateData object. We + strongly suggest that you access that data via the accessor methods below rather + than referring to the GameStateData object directly. + + Note that in classic Pacman, Pacman is always agent 0. + """ + + #################################################### + # Accessor methods: use these to access state data # + #################################################### + + # static variable keeps track of which states have had getLegalActions called + explored = set() + def getAndResetExplored(): + tmp = GameState.explored.copy() + GameState.explored = set() + return tmp + getAndResetExplored = staticmethod(getAndResetExplored) + + def getLegalActions( self, agentIndex=0 ): + """ + Returns the legal actions for the agent specified. + """ +# GameState.explored.add(self) + if self.isWin() or self.isLose(): return [] + + if agentIndex == 0: # Pacman is moving + return PacmanRules.getLegalActions( self ) + else: + return GhostRules.getLegalActions( self, agentIndex ) + + def generateSuccessor( self, agentIndex, action): + """ + Returns the successor state after the specified agent takes the action. + """ + # Check that successors exist + if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') + + # Copy current state + state = GameState(self) + + # Let agent's logic deal with its action's effects on the board + if agentIndex == 0: # Pacman is moving + state.data._eaten = [False for i in range(state.getNumAgents())] + PacmanRules.applyAction( state, action ) + else: # A ghost is moving + GhostRules.applyAction( state, action, agentIndex ) + + # Time passes + if agentIndex == 0: + state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around + else: + GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) + + # Resolve multi-agent effects + GhostRules.checkDeath( state, agentIndex ) + + # Book keeping + state.data._agentMoved = agentIndex + state.data.score += state.data.scoreChange + GameState.explored.add(self) + GameState.explored.add(state) + return state + + def getLegalPacmanActions( self ): + return self.getLegalActions( 0 ) + + def generatePacmanSuccessor( self, action ): + """ + Generates the successor state after the specified pacman move + """ + return self.generateSuccessor( 0, action ) + + def getPacmanState( self ): + """ + Returns an AgentState object for pacman (in game.py) + + state.pos gives the current position + state.direction gives the travel vector + """ + return self.data.agentStates[0].copy() + + def getPacmanPosition( self ): + return self.data.agentStates[0].getPosition() + + def getGhostStates( self ): + return self.data.agentStates[1:] + + def getGhostState( self, agentIndex ): + if agentIndex == 0 or agentIndex >= self.getNumAgents(): + raise Exception("Invalid index passed to getGhostState") + return self.data.agentStates[agentIndex] + + def getGhostPosition( self, agentIndex ): + if agentIndex == 0: + raise Exception("Pacman's index passed to getGhostPosition") + return self.data.agentStates[agentIndex].getPosition() + + def getGhostPositions(self): + return [s.getPosition() for s in self.getGhostStates()] + + def getNumAgents( self ): + return len( self.data.agentStates ) + + def getScore( self ): + return float(self.data.score) + + def getCapsules(self): + """ + Returns a list of positions (x,y) of the remaining capsules. + """ + return self.data.capsules + + def getNumFood( self ): + return self.data.food.count() + + def getFood(self): + """ + Returns a Grid of boolean food indicator variables. + + Grids can be accessed via list notation, so to check + if there is food at (x,y), just call + + currentFood = state.getFood() + if currentFood[x][y] == True: ... + """ + return self.data.food + + def getWalls(self): + """ + Returns a Grid of boolean wall indicator variables. + + Grids can be accessed via list notation, so to check + if there is a wall at (x,y), just call + + walls = state.getWalls() + if walls[x][y] == True: ... + """ + return self.data.layout.walls + + def hasFood(self, x, y): + return self.data.food[x][y] + + def hasWall(self, x, y): + return self.data.layout.walls[x][y] + + def isLose( self ): + return self.data._lose + + def isWin( self ): + return self.data._win + + ############################################# + # Helper methods: # + # You shouldn't need to call these directly # + ############################################# + + def __init__( self, prevState = None ): + """ + Generates a new state by copying information from its predecessor. + """ + if prevState != None: # Initial state + self.data = GameStateData(prevState.data) + else: + self.data = GameStateData() + + def deepCopy( self ): + state = GameState( self ) + state.data = self.data.deepCopy() + return state + + def __eq__( self, other ): + """ + Allows two states to be compared. + """ + return hasattr(other, 'data') and self.data == other.data + + def __hash__( self ): + """ + Allows states to be keys of dictionaries. + """ + return hash( self.data ) + + def __str__( self ): + + return str(self.data) + + def initialize( self, layout, numGhostAgents=1000 ): + """ + Creates an initial game state from a layout array (see layout.py). + """ + self.data.initialize(layout, numGhostAgents) + +############################################################################ +# THE HIDDEN SECRETS OF PACMAN # +# # +# You shouldn't need to look through the code in this section of the file. # +############################################################################ + +SCARED_TIME = 40 # Moves ghosts are scared +COLLISION_TOLERANCE = 0.7 # How close ghosts must be to Pacman to kill +TIME_PENALTY = 1 # Number of points lost each round + +class ClassicGameRules: + """ + These game rules manage the control flow of a game, deciding when + and how the game starts and ends. + """ + def __init__(self, timeout=30): + self.timeout = timeout + + def newGame( self, layout, pacmanAgent, ghostAgents, display, quiet = False, catchExceptions=False): + agents = [pacmanAgent] + ghostAgents[:layout.getNumGhosts()] + initState = GameState() + initState.initialize( layout, len(ghostAgents) ) + game = Game(agents, display, self, catchExceptions=catchExceptions) + game.state = initState + self.initialState = initState.deepCopy() + self.quiet = quiet + return game + + def process(self, state, game): + """ + Checks to see whether it is time to end the game. + """ + if state.isWin(): self.win(state, game) + if state.isLose(): self.lose(state, game) + + def win( self, state, game ): + if not self.quiet: print("Pacman emerges victorious! Score: %d" % state.data.score) + game.gameOver = True + + def lose( self, state, game ): + if not self.quiet: print("Pacman died! Score: %d" % state.data.score) + game.gameOver = True + + def getProgress(self, game): + return float(game.state.getNumFood()) / self.initialState.getNumFood() + + def agentCrash(self, game, agentIndex): + if agentIndex == 0: + print("Pacman crashed") + else: + print("A ghost crashed") + + def getMaxTotalTime(self, agentIndex): + return self.timeout + + def getMaxStartupTime(self, agentIndex): + return self.timeout + + def getMoveWarningTime(self, agentIndex): + return self.timeout + + def getMoveTimeout(self, agentIndex): + return self.timeout + + def getMaxTimeWarnings(self, agentIndex): + return 0 + +class PacmanRules: + """ + These functions govern how pacman interacts with his environment under + the classic game rules. + """ + PACMAN_SPEED=1 + + def getLegalActions( state ): + """ + Returns a list of possible actions. + """ + return Actions.getPossibleActions( state.getPacmanState().configuration, state.data.layout.walls ) + getLegalActions = staticmethod( getLegalActions ) + + def applyAction( state, action ): + """ + Edits the state to reflect the results of the action. + """ + legal = PacmanRules.getLegalActions( state ) + if action not in legal: + raise Exception("Illegal action " + str(action)) + + pacmanState = state.data.agentStates[0] + + # Update Configuration + vector = Actions.directionToVector( action, PacmanRules.PACMAN_SPEED ) + pacmanState.configuration = pacmanState.configuration.generateSuccessor( vector ) + + # Eat + next = pacmanState.configuration.getPosition() + nearest = nearestPoint( next ) + if manhattanDistance( nearest, next ) <= 0.5 : + # Remove food + PacmanRules.consume( nearest, state ) + applyAction = staticmethod( applyAction ) + + def consume( position, state ): + x,y = position + # Eat food + if state.data.food[x][y]: + state.data.scoreChange += 10 + state.data.food = state.data.food.copy() + state.data.food[x][y] = False + state.data._foodEaten = position + # TODO: cache numFood? + numFood = state.getNumFood() + if numFood == 0 and not state.data._lose: + state.data.scoreChange += 500 + state.data._win = True + # Eat capsule + if( position in state.getCapsules() ): + state.data.capsules.remove( position ) + state.data._capsuleEaten = position + # Reset all ghosts' scared timers + for index in range( 1, len( state.data.agentStates ) ): + state.data.agentStates[index].scaredTimer = SCARED_TIME + consume = staticmethod( consume ) + +class GhostRules: + """ + These functions dictate how ghosts interact with their environment. + """ + GHOST_SPEED=1.0 + def getLegalActions( state, ghostIndex ): + """ + Ghosts cannot stop, and cannot turn around unless they + reach a dead end, but can turn 90 degrees at intersections. + """ + conf = state.getGhostState( ghostIndex ).configuration + possibleActions = Actions.getPossibleActions( conf, state.data.layout.walls ) + reverse = Actions.reverseDirection( conf.direction ) + if Directions.STOP in possibleActions: + possibleActions.remove( Directions.STOP ) + if reverse in possibleActions and len( possibleActions ) > 1: + possibleActions.remove( reverse ) + return possibleActions + getLegalActions = staticmethod( getLegalActions ) + + def applyAction( state, action, ghostIndex): + + legal = GhostRules.getLegalActions( state, ghostIndex ) + if action not in legal: + raise Exception("Illegal ghost action " + str(action)) + + ghostState = state.data.agentStates[ghostIndex] + speed = GhostRules.GHOST_SPEED + if ghostState.scaredTimer > 0: speed /= 2.0 + vector = Actions.directionToVector( action, speed ) + ghostState.configuration = ghostState.configuration.generateSuccessor( vector ) + applyAction = staticmethod( applyAction ) + + def decrementTimer( ghostState): + timer = ghostState.scaredTimer + if timer == 1: + ghostState.configuration.pos = nearestPoint( ghostState.configuration.pos ) + ghostState.scaredTimer = max( 0, timer - 1 ) + decrementTimer = staticmethod( decrementTimer ) + + def checkDeath( state, agentIndex): + pacmanPosition = state.getPacmanPosition() + if agentIndex == 0: # Pacman just moved; Anyone can kill him + for index in range( 1, len( state.data.agentStates ) ): + ghostState = state.data.agentStates[index] + ghostPosition = ghostState.configuration.getPosition() + if GhostRules.canKill( pacmanPosition, ghostPosition ): + GhostRules.collide( state, ghostState, index ) + else: + ghostState = state.data.agentStates[agentIndex] + ghostPosition = ghostState.configuration.getPosition() + if GhostRules.canKill( pacmanPosition, ghostPosition ): + GhostRules.collide( state, ghostState, agentIndex ) + checkDeath = staticmethod( checkDeath ) + + def collide( state, ghostState, agentIndex): + if ghostState.scaredTimer > 0: + state.data.scoreChange += 200 + GhostRules.placeGhost(state, ghostState) + ghostState.scaredTimer = 0 + # Added for first-person + state.data._eaten[agentIndex] = True + else: + if not state.data._win: + state.data.scoreChange -= 500 + state.data._lose = True + collide = staticmethod( collide ) + + def canKill( pacmanPosition, ghostPosition ): + return manhattanDistance( ghostPosition, pacmanPosition ) <= COLLISION_TOLERANCE + canKill = staticmethod( canKill ) + + def placeGhost(state, ghostState): + ghostState.configuration = ghostState.start + placeGhost = staticmethod( placeGhost ) + +############################# +# FRAMEWORK TO START A GAME # +############################# + +def default(str): + return str + ' [Default: %default]' + +def parseAgentArgs(str): + if str == None: return {} + pieces = str.split(',') + opts = {} + for p in pieces: + if '=' in p: + key, val = p.split('=') + else: + key,val = p, 1 + opts[key] = val + return opts + +def readCommand( argv ): + """ + Processes the command used to run pacman from the command line. + """ + from optparse import OptionParser + usageStr = """ + USAGE: python pacman.py + EXAMPLES: (1) python pacman.py + - starts an interactive game + (2) python pacman.py --layout smallClassic --zoom 2 + OR python pacman.py -l smallClassic -z 2 + - starts an interactive game on a smaller board, zoomed in + """ + parser = OptionParser(usageStr) + + parser.add_option('-n', '--numGames', dest='numGames', type='int', + help=default('the number of GAMES to play'), metavar='GAMES', default=1) + parser.add_option('-l', '--layout', dest='layout', + help=default('the LAYOUT_FILE from which to load the map layout'), + metavar='LAYOUT_FILE', default='mediumClassic') + parser.add_option('-p', '--pacman', dest='pacman', + help=default('the agent TYPE in the pacmanAgents module to use'), + metavar='TYPE', default='KeyboardAgent') + parser.add_option('-t', '--textGraphics', action='store_true', dest='textGraphics', + help='Display output as text only', default=False) + parser.add_option('-q', '--quietTextGraphics', action='store_true', dest='quietGraphics', + help='Generate minimal output and no graphics', default=False) + parser.add_option('-g', '--ghosts', dest='ghost', + help=default('the ghost agent TYPE in the ghostAgents module to use'), + metavar = 'TYPE', default='RandomGhost') + parser.add_option('-k', '--numghosts', type='int', dest='numGhosts', + help=default('The maximum number of ghosts to use'), default=4) + parser.add_option('-z', '--zoom', type='float', dest='zoom', + help=default('Zoom the size of the graphics window'), default=1.0) + parser.add_option('-f', '--fixRandomSeed', action='store_true', dest='fixRandomSeed', + help='Fixes the random seed to always play the same game', default=False) + parser.add_option('-r', '--recordActions', action='store_true', dest='record', + help='Writes game histories to a file (named by the time they were played)', default=False) + parser.add_option('--replay', dest='gameToReplay', + help='A recorded game file (pickle) to replay', default=None) + parser.add_option('-a','--agentArgs',dest='agentArgs', + help='Comma separated values sent to agent. e.g. "opt1=val1,opt2,opt3=val3"') + parser.add_option('-x', '--numTraining', dest='numTraining', type='int', + help=default('How many episodes are training (suppresses output)'), default=0) + parser.add_option('--frameTime', dest='frameTime', type='float', + help=default('Time to delay between frames; <0 means keyboard'), default=0.1) + parser.add_option('-c', '--catchExceptions', action='store_true', dest='catchExceptions', + help='Turns on exception handling and timeouts during games', default=False) + parser.add_option('--timeout', dest='timeout', type='int', + help=default('Maximum length of time an agent can spend computing in a single game'), default=30) + + options, otherjunk = parser.parse_args(argv) + if len(otherjunk) != 0: + raise Exception('Command line input not understood: ' + str(otherjunk)) + args = dict() + + # Fix the random seed + if options.fixRandomSeed: random.seed('cs188') + + # Choose a layout + args['layout'] = layout.getLayout( options.layout ) + if args['layout'] == None: raise Exception("The layout " + options.layout + " cannot be found") + + # Choose a Pacman agent + noKeyboard = options.gameToReplay == None and (options.textGraphics or options.quietGraphics) + pacmanType = loadAgent(options.pacman, noKeyboard) + agentOpts = parseAgentArgs(options.agentArgs) + if options.numTraining > 0: + args['numTraining'] = options.numTraining + if 'numTraining' not in agentOpts: agentOpts['numTraining'] = options.numTraining + pacman = pacmanType(**agentOpts) # Instantiate Pacman with agentArgs + args['pacman'] = pacman + + # Don't display training games + if 'numTrain' in agentOpts: + options.numQuiet = int(agentOpts['numTrain']) + options.numIgnore = int(agentOpts['numTrain']) + + # Choose a ghost agent + ghostType = loadAgent(options.ghost, noKeyboard) + args['ghosts'] = [ghostType( i+1 ) for i in range( options.numGhosts )] + + # Choose a display format + if options.quietGraphics: + import textDisplay + args['display'] = textDisplay.NullGraphics() + elif options.textGraphics: + import textDisplay + textDisplay.SLEEP_TIME = options.frameTime + args['display'] = textDisplay.PacmanGraphics() + else: + import graphicsDisplay + args['display'] = graphicsDisplay.PacmanGraphics(options.zoom, frameTime = options.frameTime) + args['numGames'] = options.numGames + args['record'] = options.record + args['catchExceptions'] = options.catchExceptions + args['timeout'] = options.timeout + + # Special case: recorded games don't use the runGames method or args structure + if options.gameToReplay != None: + print('Replaying recorded game %s.' % options.gameToReplay) + import pickle + f = open(options.gameToReplay, 'rb') + try: recorded = pickle.load(f) + finally: f.close() + recorded['display'] = args['display'] + replayGame(**recorded) + sys.exit(0) + + return args + +def loadAgent(pacman, nographics): + # Looks through all pythonPath Directories for the right module, + pythonPathStr = os.path.expandvars("$PYTHONPATH") + if pythonPathStr.find(';') == -1: + pythonPathDirs = pythonPathStr.split(':') + else: + pythonPathDirs = pythonPathStr.split(';') + pythonPathDirs.append('.') + + for moduleDir in pythonPathDirs: + if not os.path.isdir(moduleDir): continue + moduleNames = [f for f in os.listdir(moduleDir) if f.endswith('gents.py')] + for modulename in moduleNames: + try: + module = __import__(modulename[:-3]) + except ImportError: + continue + if pacman in dir(module): + if nographics and modulename == 'keyboardAgents.py': + raise Exception('Using the keyboard requires graphics (not text display)') + return getattr(module, pacman) + raise Exception('The agent ' + pacman + ' is not specified in any *Agents.py.') + +def replayGame( layout, actions, display ): + import pacmanAgents, ghostAgents + rules = ClassicGameRules() + agents = [pacmanAgents.GreedyAgent()] + [ghostAgents.RandomGhost(i+1) for i in range(layout.getNumGhosts())] + game = rules.newGame( layout, agents[0], agents[1:], display ) + state = game.state + display.initialize(state.data) + + for action in actions: + # Execute the action + state = state.generateSuccessor( *action ) + # Change the display + display.update( state.data ) + # Allow for game specific conditions (winning, losing, etc.) + rules.process(state, game) + + display.finish() + +def runGames( layout, pacman, ghosts, display, numGames, record, numTraining = 0, catchExceptions=False, timeout=30 ): + import __main__ + __main__.__dict__['_display'] = display + + rules = ClassicGameRules(timeout) + games = [] + + for i in range( numGames ): + beQuiet = i < numTraining + if beQuiet: + # Suppress output and graphics + import textDisplay + gameDisplay = textDisplay.NullGraphics() + rules.quiet = True + else: + gameDisplay = display + rules.quiet = False + game = rules.newGame( layout, pacman, ghosts, gameDisplay, beQuiet, catchExceptions) + game.run() + if not beQuiet: games.append(game) + + if record: + import time, pickle + fname = ('recorded-game-%d' % (i + 1)) + '-'.join([str(t) for t in time.localtime()[1:6]]) + f = open(fname, 'wb') + components = {'layout': layout, 'actions': game.moveHistory} + pickle.dump(components, f) + f.close() + + if (numGames-numTraining) > 0: + scores = [game.state.getScore() for game in games] + wins = [game.state.isWin() for game in games] + winRate = wins.count(True)/ float(len(wins)) + print('Average Score:', sum(scores) / float(len(scores))) + print('Scores: ', ', '.join([str(score) for score in scores])) + print('Win Rate: %d/%d (%.2f)' % (wins.count(True), len(wins), winRate)) + print('Record: ', ', '.join([ ['Loss', 'Win'][int(w)] for w in wins])) + + return games + +if __name__ == '__main__': + """ + The main function called when pacman.py is run + from the command line: + + > python pacman.py + + See the usage string for more details. + + > python pacman.py --help + """ + args = readCommand( sys.argv[1:] ) # Get game components based on input + runGames( **args ) + + # import cProfile + # cProfile.run("runGames( **args )") + pass diff --git a/proj1/pacmanAgents.py b/proj1/pacmanAgents.py new file mode 100755 index 0000000..ae97634 --- /dev/null +++ b/proj1/pacmanAgents.py @@ -0,0 +1,52 @@ +# pacmanAgents.py +# --------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +from pacman import Directions +from game import Agent +import random +import game +import util + +class LeftTurnAgent(game.Agent): + "An agent that turns left at every opportunity" + + def getAction(self, state): + legal = state.getLegalPacmanActions() + current = state.getPacmanState().configuration.direction + if current == Directions.STOP: current = Directions.NORTH + left = Directions.LEFT[current] + if left in legal: return left + if current in legal: return current + if Directions.RIGHT[current] in legal: return Directions.RIGHT[current] + if Directions.LEFT[left] in legal: return Directions.LEFT[left] + return Directions.STOP + +class GreedyAgent(Agent): + def __init__(self, evalFn="scoreEvaluation"): + self.evaluationFunction = util.lookup(evalFn, globals()) + assert self.evaluationFunction != None + + def getAction(self, state): + # Generate candidate actions + legal = state.getLegalPacmanActions() + if Directions.STOP in legal: legal.remove(Directions.STOP) + + successors = [(state.generateSuccessor(0, action), action) for action in legal] + scored = [(self.evaluationFunction(state), action) for state, action in successors] + bestScore = max(scored)[0] + bestActions = [pair[1] for pair in scored if pair[0] == bestScore] + return random.choice(bestActions) + +def scoreEvaluation(state): + return state.getScore() diff --git a/proj1/projectParams.py b/proj1/projectParams.py new file mode 100755 index 0000000..dc3e9d1 --- /dev/null +++ b/proj1/projectParams.py @@ -0,0 +1,18 @@ +# projectParams.py +# ---------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +STUDENT_CODE_DEFAULT = 'searchAgents.py,search.py' +PROJECT_TEST_CLASSES = 'searchTestClasses.py' +PROJECT_NAME = 'Project 1: Search' +BONUS_PIC = False diff --git a/proj1/search.py b/proj1/search.py new file mode 100755 index 0000000..86426d5 --- /dev/null +++ b/proj1/search.py @@ -0,0 +1,277 @@ +# search.py +# --------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +""" +In search.py, you will implement generic search algorithms which are called by +Pacman agents (in searchAgents.py). +""" + +import util + +class SearchProblem: + """ + This class outlines the structure of a search problem, but doesn't implement + any of the methods (in object-oriented terminology: an abstract class). + + You do not need to change anything in this class, ever. + """ + + def getStartState(self): + """ + Returns the start state for the search problem. + """ + util.raiseNotDefined() + + def isGoalState(self, state): + """ + state: Search state + + Returns True if and only if the state is a valid goal state. + """ + util.raiseNotDefined() + + def getSuccessors(self, state): + """ + state: Search state + + For a given state, this should return a list of triples, (successor, + action, stepCost), where 'successor' is a successor to the current + state, 'action' is the action required to get there, and 'stepCost' is + the incremental cost of expanding to that successor. + """ + util.raiseNotDefined() + + def getCostOfActions(self, actions): + """ + actions: A list of actions to take + + This method returns the total cost of a particular sequence of actions. + The sequence must be composed of legal moves. + """ + util.raiseNotDefined() + + +def tinyMazeSearch(problem): + """ + Returns a sequence of moves that solves tinyMaze. For any other maze, the + sequence of moves will be incorrect, so only use this for tinyMaze. + """ + from game import Directions + s = Directions.SOUTH + w = Directions.WEST + return [s, s, w, s, w, w, s, w] + +def depthFirstSearch(problem: SearchProblem): + """ + Search the deepest nodes in the search tree first. + + Your search algorithm needs to return a list of actions that reaches the + goal. Make sure to implement a graph search algorithm. + + To get started, you might want to try some of these simple commands to + understand the search problem that is being passed in: + + print("Start:", problem.getStartState()) + print("Is the start a goal?", problem.isGoalState(problem.getStartState())) + print("Start's successors:", problem.getSuccessors(problem.getStartState())) + """ + # 初始化栈用于深度优先搜索,存储(状态, 路径)元组 + # 使用栈实现LIFO(后进先出)的搜索策略 + fringe = util.Stack() + + # 记录已访问的状态,避免重复搜索(图搜索) + visited = set() + + # 获取起始状态并加入栈中,初始路径为空 + startState = problem.getStartState() + fringe.push((startState, [])) + + # 当栈不为空时继续搜索 + while not fringe.isEmpty(): + # 弹出栈顶元素(当前状态和到达该状态的路径) + currentState, actions = fringe.pop() + + # 如果当前状态已经访问过,跳过 + if currentState in visited: + continue + + # 标记当前状态为已访问 + visited.add(currentState) + + # 检查是否到达目标状态 + if problem.isGoalState(currentState): + return actions + + # 获取当前状态的所有后继状态 + successors = problem.getSuccessors(currentState) + + # 将所有未访问的后继状态加入栈中 + for successor, action, cost in successors: + if successor not in visited: + # 构建新的路径:当前路径 + 新动作 + newActions = actions + [action] + fringe.push((successor, newActions)) + + # 如果栈为空仍未找到目标,返回空列表 + return [] + +def breadthFirstSearch(problem: SearchProblem): + """Search the shallowest nodes in the search tree first.""" + # 初始化队列用于广度优先搜索,存储(状态, 路径)元组 + # 使用队列实现FIFO(先进先出)的搜索策略 + fringe = util.Queue() + + # 记录已访问的状态,避免重复搜索(图搜索) + visited = set() + + # 获取起始状态并加入队列中,初始路径为空 + startState = problem.getStartState() + fringe.push((startState, [])) + + # 当队列不为空时继续搜索 + while not fringe.isEmpty(): + # 弹出队列头部元素(当前状态和到达该状态的路径) + currentState, actions = fringe.pop() + + # 如果当前状态已经访问过,跳过 + if currentState in visited: + continue + + # 标记当前状态为已访问 + visited.add(currentState) + + # 检查是否到达目标状态 + if problem.isGoalState(currentState): + return actions + + # 获取当前状态的所有后继状态 + successors = problem.getSuccessors(currentState) + + # 将所有未访问的后继状态加入队列中 + for successor, action, cost in successors: + if successor not in visited: + # 构建新的路径:当前路径 + 新动作 + newActions = actions + [action] + fringe.push((successor, newActions)) + + # 如果队列为空仍未找到目标,返回空列表 + return [] + +def uniformCostSearch(problem: SearchProblem): + """Search the node of least total cost first.""" + # 初始化优先队列用于统一代价搜索,存储(状态, 路径, 累积代价)元组 + # 使用优先队列实现按代价优先搜索的策略 + fringe = util.PriorityQueue() + + # 记录已访问的状态及其最小代价,避免重复搜索(图搜索) + visited = {} + + # 获取起始状态并加入优先队列中,初始路径为空,初始代价为0 + startState = problem.getStartState() + fringe.push((startState, [], 0), 0) # (状态, 路径, 累积代价), 优先级=累积代价 + + # 当优先队列不为空时继续搜索 + while not fringe.isEmpty(): + # 弹出优先级最高的元素(累积代价最小的元素) + currentState, actions, currentCost = fringe.pop() + + # 如果当前状态已经访问过,且当前代价大于等于已访问的代价,跳过 + if currentState in visited and currentCost >= visited[currentState]: + continue + + # 记录当前状态及其最小代价 + visited[currentState] = currentCost + + # 检查是否到达目标状态 + if problem.isGoalState(currentState): + return actions + + # 获取当前状态的所有后继状态 + successors = problem.getSuccessors(currentState) + + # 将所有后继状态加入优先队列中 + for successor, action, stepCost in successors: + # 计算新的累积代价 + newCost = currentCost + stepCost + # 构建新的路径:当前路径 + 新动作 + newActions = actions + [action] + # 将后继状态加入优先队列,优先级为新的累积代价 + fringe.push((successor, newActions, newCost), newCost) + + # 如果优先队列为空仍未找到目标,返回空列表 + return [] + +def nullHeuristic(state, problem=None): + """ + A heuristic function estimates the cost from the current state to the nearest + goal in the provided SearchProblem. This heuristic is trivial. + """ + return 0 + +def aStarSearch(problem: SearchProblem, heuristic=nullHeuristic): + """Search the node that has the lowest combined cost and heuristic first.""" + # 初始化优先队列用于A*搜索,存储(状态, 路径, 累积代价)元组 + # 使用优先队列实现按f(n)=g(n)+h(n)优先搜索的策略 + # 其中g(n)是实际代价,h(n)是启发式估计代价 + fringe = util.PriorityQueue() + + # 记录已访问的状态及其最小g(n)代价,避免重复搜索(图搜索) + visited = {} + + # 获取起始状态并加入优先队列中,初始路径为空,初始g(n)代价为0 + startState = problem.getStartState() + startHeuristic = heuristic(startState, problem) + fringe.push((startState, [], 0), startHeuristic) # (状态, 路径, g(n)), 优先级=f(n)=g(n)+h(n) + + # 当优先队列不为空时继续搜索 + while not fringe.isEmpty(): + # 弹出优先级最高的元素(f(n)值最小的元素) + currentState, actions, currentCost = fringe.pop() + + # 如果当前状态已经访问过,且当前g(n)代价大于等于已访问的g(n)代价,跳过 + if currentState in visited and currentCost >= visited[currentState]: + continue + + # 记录当前状态及其最小g(n)代价 + visited[currentState] = currentCost + + # 检查是否到达目标状态 + if problem.isGoalState(currentState): + return actions + + # 获取当前状态的所有后继状态 + successors = problem.getSuccessors(currentState) + + # 将所有后继状态加入优先队列中 + for successor, action, stepCost in successors: + # 计算新的g(n)代价 + newCost = currentCost + stepCost + # 计算新的h(n)启发式估计代价 + newHeuristic = heuristic(successor, problem) + # 计算新的f(n)值 = g(n) + h(n) + fValue = newCost + newHeuristic + # 构建新的路径:当前路径 + 新动作 + newActions = actions + [action] + # 将后继状态加入优先队列,优先级为f(n)值 + fringe.push((successor, newActions, newCost), fValue) + + # 如果优先队列为空仍未找到目标,返回空列表 + return [] + + +# Abbreviations +bfs = breadthFirstSearch +dfs = depthFirstSearch +astar = aStarSearch +ucs = uniformCostSearch diff --git a/proj1/searchAgents.py b/proj1/searchAgents.py new file mode 100755 index 0000000..35e775e --- /dev/null +++ b/proj1/searchAgents.py @@ -0,0 +1,715 @@ +# searchAgents.py +# --------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +""" +This file contains all of the agents that can be selected to control Pacman. To +select an agent, use the '-p' option when running pacman.py. Arguments can be +passed to your agent using '-a'. For example, to load a SearchAgent that uses +depth first search (dfs), run the following command: + +> python pacman.py -p SearchAgent -a fn=depthFirstSearch + +Commands to invoke other search strategies can be found in the project +description. + +Please only change the parts of the file you are asked to. Look for the lines +that say + +"*** YOUR CODE HERE ***" + +The parts you fill in start about 3/4 of the way down. Follow the project +description for details. + +Good luck and happy searching! +""" + +from typing import List, Tuple, Any +from game import Directions +from game import Agent +from game import Actions +import util +import time +import search +import pacman + +class GoWestAgent(Agent): + "An agent that goes West until it can't." + + def getAction(self, state): + "The agent receives a GameState (defined in pacman.py)." + if Directions.WEST in state.getLegalPacmanActions(): + return Directions.WEST + else: + return Directions.STOP + +####################################################### +# This portion is written for you, but will only work # +# after you fill in parts of search.py # +####################################################### + +class SearchAgent(Agent): + """ + This very general search agent finds a path using a supplied search + algorithm for a supplied search problem, then returns actions to follow that + path. + + As a default, this agent runs DFS on a PositionSearchProblem to find + location (1,1) + + Options for fn include: + depthFirstSearch or dfs + breadthFirstSearch or bfs + + + Note: You should NOT change any code in SearchAgent + """ + + def __init__(self, fn='depthFirstSearch', prob='PositionSearchProblem', heuristic='nullHeuristic'): + # Warning: some advanced Python magic is employed below to find the right functions and problems + + # Get the search function from the name and heuristic + if fn not in dir(search): + raise AttributeError(fn + ' is not a search function in search.py.') + func = getattr(search, fn) + if 'heuristic' not in func.__code__.co_varnames: + print('[SearchAgent] using function ' + fn) + self.searchFunction = func + else: + if heuristic in globals().keys(): + heur = globals()[heuristic] + elif heuristic in dir(search): + heur = getattr(search, heuristic) + else: + raise AttributeError(heuristic + ' is not a function in searchAgents.py or search.py.') + print('[SearchAgent] using function %s and heuristic %s' % (fn, heuristic)) + # Note: this bit of Python trickery combines the search algorithm and the heuristic + self.searchFunction = lambda x: func(x, heuristic=heur) + + # Get the search problem type from the name + if prob not in globals().keys() or not prob.endswith('Problem'): + raise AttributeError(prob + ' is not a search problem type in SearchAgents.py.') + self.searchType = globals()[prob] + print('[SearchAgent] using problem type ' + prob) + + def registerInitialState(self, state): + """ + This is the first time that the agent sees the layout of the game + board. Here, we choose a path to the goal. In this phase, the agent + should compute the path to the goal and store it in a local variable. + All of the work is done in this method! + + state: a GameState object (pacman.py) + """ + if self.searchFunction == None: raise Exception("No search function provided for SearchAgent") + starttime = time.time() + problem = self.searchType(state) # Makes a new search problem + self.actions = self.searchFunction(problem) # Find a path + if self.actions == None: + self.actions = [] + totalCost = problem.getCostOfActions(self.actions) + print('Path found with total cost of %d in %.1f seconds' % (totalCost, time.time() - starttime)) + if '_expanded' in dir(problem): print('Search nodes expanded: %d' % problem._expanded) + + def getAction(self, state): + """ + Returns the next action in the path chosen earlier (in + registerInitialState). Return Directions.STOP if there is no further + action to take. + + state: a GameState object (pacman.py) + """ + if 'actionIndex' not in dir(self): self.actionIndex = 0 + i = self.actionIndex + self.actionIndex += 1 + if i < len(self.actions): + return self.actions[i] + else: + return Directions.STOP + +class PositionSearchProblem(search.SearchProblem): + """ + A search problem defines the state space, start state, goal test, successor + function and cost function. This search problem can be used to find paths + to a particular point on the pacman board. + + The state space consists of (x,y) positions in a pacman game. + + Note: this search problem is fully specified; you should NOT change it. + """ + + def __init__(self, gameState, costFn = lambda x: 1, goal=(1,1), start=None, warn=True, visualize=True): + """ + Stores the start and goal. + + gameState: A GameState object (pacman.py) + costFn: A function from a search state (tuple) to a non-negative number + goal: A position in the gameState + """ + self.walls = gameState.getWalls() + self.startState = gameState.getPacmanPosition() + if start != None: self.startState = start + self.goal = goal + self.costFn = costFn + self.visualize = visualize + if warn and (gameState.getNumFood() != 1 or not gameState.hasFood(*goal)): + print('Warning: this does not look like a regular search maze') + + # For display purposes + self._visited, self._visitedlist, self._expanded = {}, [], 0 # DO NOT CHANGE + + def getStartState(self): + return self.startState + + def isGoalState(self, state): + isGoal = state == self.goal + + # For display purposes only + if isGoal and self.visualize: + self._visitedlist.append(state) + import __main__ + if '_display' in dir(__main__): + if 'drawExpandedCells' in dir(__main__._display): #@UndefinedVariable + __main__._display.drawExpandedCells(self._visitedlist) #@UndefinedVariable + + return isGoal + + def getSuccessors(self, state): + """ + Returns successor states, the actions they require, and a cost of 1. + + As noted in search.py: + For a given state, this should return a list of triples, + (successor, action, stepCost), where 'successor' is a + successor to the current state, 'action' is the action + required to get there, and 'stepCost' is the incremental + cost of expanding to that successor + """ + + successors = [] + for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: + x,y = state + dx, dy = Actions.directionToVector(action) + nextx, nexty = int(x + dx), int(y + dy) + if not self.walls[nextx][nexty]: + nextState = (nextx, nexty) + cost = self.costFn(nextState) + successors.append( ( nextState, action, cost) ) + + # Bookkeeping for display purposes + self._expanded += 1 # DO NOT CHANGE + if state not in self._visited: + self._visited[state] = True + self._visitedlist.append(state) + + return successors + + def getCostOfActions(self, actions): + """ + Returns the cost of a particular sequence of actions. If those actions + include an illegal move, return 999999. + """ + if actions == None: return 999999 + x,y= self.getStartState() + cost = 0 + for action in actions: + # Check figure out the next state and see whether its' legal + dx, dy = Actions.directionToVector(action) + x, y = int(x + dx), int(y + dy) + if self.walls[x][y]: return 999999 + cost += self.costFn((x,y)) + return cost + +class StayEastSearchAgent(SearchAgent): + """ + An agent for position search with a cost function that penalizes being in + positions on the West side of the board. + + The cost function for stepping into a position (x,y) is 1/2^x. + """ + def __init__(self): + self.searchFunction = search.uniformCostSearch + costFn = lambda pos: .5 ** pos[0] + self.searchType = lambda state: PositionSearchProblem(state, costFn, (1, 1), None, False) + +class StayWestSearchAgent(SearchAgent): + """ + An agent for position search with a cost function that penalizes being in + positions on the East side of the board. + + The cost function for stepping into a position (x,y) is 2^x. + """ + def __init__(self): + self.searchFunction = search.uniformCostSearch + costFn = lambda pos: 2 ** pos[0] + self.searchType = lambda state: PositionSearchProblem(state, costFn) + +def manhattanHeuristic(position, problem, info={}): + "The Manhattan distance heuristic for a PositionSearchProblem" + xy1 = position + xy2 = problem.goal + return abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1]) + +def euclideanHeuristic(position, problem, info={}): + "The Euclidean distance heuristic for a PositionSearchProblem" + xy1 = position + xy2 = problem.goal + return ( (xy1[0] - xy2[0]) ** 2 + (xy1[1] - xy2[1]) ** 2 ) ** 0.5 + +##################################################### +# This portion is incomplete. Time to write code! # +##################################################### + +class CornersProblem(search.SearchProblem): + """ + This search problem finds paths through all four corners of a layout. + + You must select a suitable state space and successor function + """ + + def __init__(self, startingGameState: pacman.GameState): + """ + Stores the walls, pacman's starting position and corners. + """ + self.walls = startingGameState.getWalls() + self.startingPosition = startingGameState.getPacmanPosition() + top, right = self.walls.height-2, self.walls.width-2 + self.corners = ((1,1), (1,top), (right, 1), (right, top)) + for corner in self.corners: + if not startingGameState.hasFood(*corner): + print('Warning: no food in corner ' + str(corner)) + self._expanded = 0 # DO NOT CHANGE; Number of search nodes expanded + + def getStartState(self): + """ + Returns the start state (in your state space, not the full Pacman state + space) + """ + # 状态表示为:(当前位置, 已访问的角落元组) + # 已访问的角落用四个布尔值表示,分别对应四个角落是否已被访问 + # 初始状态下,只有起始位置被访问,没有角落被访问 + cornersVisited = tuple([corner == self.startingPosition for corner in self.corners]) + return (self.startingPosition, cornersVisited) + + def isGoalState(self, state: Any): + """ + Returns whether this search state is a goal state of the problem. + """ + # 目标状态是所有四个角落都已被访问 + # 状态的第二个元素是一个元组,表示四个角落的访问状态 + # 如果所有四个值都为True,表示所有角落都已访问 + _, cornersVisited = state + return all(cornersVisited) + + def getSuccessors(self, state: Any): + """ + Returns successor states, the actions they require, and a cost of 1. + + As noted in search.py: + For a given state, this should return a list of triples, (successor, + action, stepCost), where 'successor' is a successor to the current + state, 'action' is the action required to get there, and 'stepCost' + is the incremental cost of expanding to that successor + """ + + successors = [] + # 从当前状态中提取当前位置和角落访问状态 + currentPosition, cornersVisited = state + + for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: + # 计算移动后的位置 + x, y = currentPosition + dx, dy = Actions.directionToVector(action) + nextx, nexty = int(x + dx), int(y + dy) + + # 检查是否会撞墙 + if not self.walls[nextx][nexty]: + # 创建新的角落访问状态列表 + nextPosition = (nextx, nexty) + newCornersVisited = list(cornersVisited) + + # 检查新位置是否是某个角落,如果是,标记为已访问 + for i, corner in enumerate(self.corners): + if nextPosition == corner and not newCornersVisited[i]: + newCornersVisited[i] = True + + # 创建后继状态:(新位置, 新的角落访问状态) + successorState = (nextPosition, tuple(newCornersVisited)) + + # 将后继状态、动作和代价(固定为1)添加到后继列表中 + successors.append((successorState, action, 1)) + + self._expanded += 1 # DO NOT CHANGE + return successors + + def getCostOfActions(self, actions): + """ + Returns the cost of a particular sequence of actions. If those actions + include an illegal move, return 999999. This is implemented for you. + """ + if actions == None: return 999999 + x,y= self.startingPosition + for action in actions: + dx, dy = Actions.directionToVector(action) + x, y = int(x + dx), int(y + dy) + if self.walls[x][y]: return 999999 + return len(actions) + + +def cornersHeuristic(state: Any, problem: CornersProblem): + """ + A heuristic for the CornersProblem that you defined. + + state: The current search state + (a data structure you chose in your search problem) + + problem: The CornersProblem instance for this layout. + + This function should always return a number that is a lower bound on the + shortest path from the state to a goal of the problem; i.e. it should be + admissible (as well as consistent). + """ + # 从状态中提取当前位置和角落访问状态 + currentPosition, cornersVisited = state + corners = problem.corners + walls = problem.walls + + # 如果所有角落都已访问,返回0 + if all(cornersVisited): + return 0 + + # 找出所有未访问的角落 + unvisitedCorners = [] + for i, corner in enumerate(corners): + if not cornersVisited[i]: + unvisitedCorners.append(corner) + + # 如果没有未访问的角落,返回0 + if not unvisitedCorners: + return 0 + + # 启发式策略:计算当前位置到最远未访问角落的曼哈顿距离 + # 这是一个可接受的启发式,因为曼哈顿距离是实际最短路径的下界 + maxDistance = 0 + for corner in unvisitedCorners: + distance = util.manhattanDistance(currentPosition, corner) + maxDistance = max(maxDistance, distance) + + # 为了改进启发式,我们还可以考虑未访问角落之间的最大距离 + # 这样可以更好地估计访问所有剩余角落所需的总距离 + maxCornerDistance = 0 + for i in range(len(unvisitedCorners)): + for j in range(i + 1, len(unvisitedCorners)): + distance = util.manhattanDistance(unvisitedCorners[i], unvisitedCorners[j]) + maxCornerDistance = max(maxCornerDistance, distance) + + # 返回两个距离中的较大值作为启发式估计 + # 这确保了启发式是可接受的(不会高估实际代价) + return max(maxDistance, maxCornerDistance) + +class AStarCornersAgent(SearchAgent): + "A SearchAgent for FoodSearchProblem using A* and your foodHeuristic" + def __init__(self): + self.searchFunction = lambda prob: search.aStarSearch(prob, cornersHeuristic) + self.searchType = CornersProblem + +class FoodSearchProblem: + """ + A search problem associated with finding the a path that collects all of the + food (dots) in a Pacman game. + + A search state in this problem is a tuple ( pacmanPosition, foodGrid ) where + pacmanPosition: a tuple (x,y) of integers specifying Pacman's position + foodGrid: a Grid (see game.py) of either True or False, specifying remaining food + """ + def __init__(self, startingGameState: pacman.GameState): + self.start = (startingGameState.getPacmanPosition(), startingGameState.getFood()) + self.walls = startingGameState.getWalls() + self.startingGameState = startingGameState + self._expanded = 0 # DO NOT CHANGE + self.heuristicInfo = {} # A dictionary for the heuristic to store information + + def getStartState(self): + return self.start + + def isGoalState(self, state): + return state[1].count() == 0 + + def getSuccessors(self, state): + "Returns successor states, the actions they require, and a cost of 1." + successors = [] + self._expanded += 1 # DO NOT CHANGE + for direction in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: + x,y = state[0] + dx, dy = Actions.directionToVector(direction) + nextx, nexty = int(x + dx), int(y + dy) + if not self.walls[nextx][nexty]: + nextFood = state[1].copy() + nextFood[nextx][nexty] = False + successors.append( ( ((nextx, nexty), nextFood), direction, 1) ) + return successors + + def getCostOfActions(self, actions): + """Returns the cost of a particular sequence of actions. If those actions + include an illegal move, return 999999""" + x,y= self.getStartState()[0] + cost = 0 + for action in actions: + # figure out the next state and see whether it's legal + dx, dy = Actions.directionToVector(action) + x, y = int(x + dx), int(y + dy) + if self.walls[x][y]: + return 999999 + cost += 1 + return cost + +class AStarFoodSearchAgent(SearchAgent): + "A SearchAgent for FoodSearchProblem using A* and your foodHeuristic" + def __init__(self): + self.searchFunction = lambda prob: search.aStarSearch(prob, foodHeuristic) + self.searchType = FoodSearchProblem + +def foodHeuristic(state: Tuple[Tuple, List[List]], problem: FoodSearchProblem): + """ + Your heuristic for the FoodSearchProblem goes here. + + This heuristic must be consistent to ensure correctness. First, try to come + up with an admissible heuristic; almost all admissible heuristics will be + consistent as well. + + If using A* ever finds a solution that is worse uniform cost search finds, + your heuristic is *not* consistent, and probably not admissible! On the + other hand, inadmissible or inconsistent heuristics may find optimal + solutions, so be careful. + + The state is a tuple ( pacmanPosition, foodGrid ) where foodGrid is a Grid + (see game.py) of either True or False. You can call foodGrid.asList() to get + a list of food coordinates instead. + + If you want access to info like walls, capsules, etc., you can query the + problem. For example, problem.walls gives you a Grid of where the walls + are. + + If you want to *store* information to be reused in other calls to the + heuristic, there is a dictionary called problem.heuristicInfo that you can + use. For example, if you only want to count the walls once and store that + value, try: problem.heuristicInfo['wallCount'] = problem.walls.count() + Subsequent calls to this heuristic can access + problem.heuristicInfo['wallCount'] + """ + position, foodGrid = state + + # 获取所有剩余食物的位置 + foodList = foodGrid.asList() + + # 如果没有剩余食物,返回0 + if not foodList: + return 0 + + # 使用保守但可接受的启发式策略 + # 策略1:计算到最远食物的曼哈顿距离 + # 这是一个可接受的启发式,因为曼哈顿距离是实际最短路径的下界 + maxDistance = 0 + for food in foodList: + distance = util.manhattanDistance(position, food) + maxDistance = max(maxDistance, distance) + + # 策略2:计算食物之间的最大距离 + # 这提供了一个访问所有食物所需路径的下界估计 + maxFoodDistance = 0 + if len(foodList) > 1: + for i in range(len(foodList)): + for j in range(i + 1, len(foodList)): + distance = util.manhattanDistance(foodList[i], foodList[j]) + maxFoodDistance = max(maxFoodDistance, distance) + + # 策略3:使用食物数量作为基础代价 + # 每个食物至少需要一步来吃掉 + foodCount = len(foodList) + + # 返回三个策略中的最大值,确保启发式是可接受的 + # 这样可以更好地估计总代价,同时保持可接受性和一致性 + return max(maxDistance, maxFoodDistance, foodCount) + +def mstHeuristic(position, foodList): + """ + 计算基于最小生成树的启发式值 + 使用Prim算法的简化版本计算连接Pacman和所有食物点的最小生成树 + """ + if not foodList: + return 0 + + # 将Pacman位置添加到食物列表中 + allPoints = [position] + foodList + n = len(allPoints) + + # Prim算法计算最小生成树 + visited = [False] * n + minDistances = [float('inf')] * n + minDistances[0] = 0 # 从Pacman位置开始 + totalDistance = 0 + + for _ in range(n): + # 找到未访问节点中距离最小的节点 + minDist = float('inf') + minIndex = -1 + for i in range(n): + if not visited[i] and minDistances[i] < minDist: + minDist = minDistances[i] + minIndex = i + + if minIndex == -1: + break + + visited[minIndex] = True + totalDistance += minDist + + # 更新相邻节点的最小距离 + for i in range(n): + if not visited[i]: + distance = util.manhattanDistance(allPoints[minIndex], allPoints[i]) + if distance < minDistances[i]: + minDistances[i] = distance + + return totalDistance + +def calculateMSTDistance(foodList, position): + """ + 计算连接Pacman位置和所有食物点的最小生成树的近似总距离 + 使用Prim算法的简化版本 + """ + if not foodList: + return 0 + + # 将Pacman位置添加到食物列表中 + allPoints = [position] + foodList + n = len(allPoints) + + # Prim算法计算最小生成树 + visited = [False] * n + minDistances = [float('inf')] * n + minDistances[0] = 0 # 从Pacman位置开始 + totalDistance = 0 + + for _ in range(n): + # 找到未访问节点中距离最小的节点 + minDist = float('inf') + minIndex = -1 + for i in range(n): + if not visited[i] and minDistances[i] < minDist: + minDist = minDistances[i] + minIndex = i + + if minIndex == -1: + break + + visited[minIndex] = True + totalDistance += minDist + + # 更新相邻节点的最小距离 + for i in range(n): + if not visited[i]: + distance = util.manhattanDistance(allPoints[minIndex], allPoints[i]) + if distance < minDistances[i]: + minDistances[i] = distance + + return totalDistance + +class ClosestDotSearchAgent(SearchAgent): + "Search for all food using a sequence of searches" + def registerInitialState(self, state): + self.actions = [] + currentState = state + while(currentState.getFood().count() > 0): + nextPathSegment = self.findPathToClosestDot(currentState) # The missing piece + self.actions += nextPathSegment + for action in nextPathSegment: + legal = currentState.getLegalActions() + if action not in legal: + t = (str(action), str(currentState)) + raise Exception('findPathToClosestDot returned an illegal move: %s!\n%s' % t) + currentState = currentState.generateSuccessor(0, action) + self.actionIndex = 0 + print('Path found with cost %d.' % len(self.actions)) + + def findPathToClosestDot(self, gameState: pacman.GameState): + """ + Returns a path (a list of actions) to the closest dot, starting from + gameState. + """ + # Here are some useful elements of the startState + startPosition = gameState.getPacmanPosition() + food = gameState.getFood() + walls = gameState.getWalls() + problem = AnyFoodSearchProblem(gameState) + + # 使用广度优先搜索找到最近的食物点 + # BFS保证找到最短路径(步数最少) + path = search.bfs(problem) + + return path + +class AnyFoodSearchProblem(PositionSearchProblem): + """ + A search problem for finding a path to any food. + + This search problem is just like the PositionSearchProblem, but has a + different goal test, which you need to fill in below. The state space and + successor function do not need to be changed. + + The class definition above, AnyFoodSearchProblem(PositionSearchProblem), + inherits the methods of the PositionSearchProblem. + + You can use this search problem to help you fill in the findPathToClosestDot + method. + """ + + def __init__(self, gameState): + "Stores information from the gameState. You don't need to change this." + # Store the food for later reference + self.food = gameState.getFood() + + # Store info for the PositionSearchProblem (no need to change this) + self.walls = gameState.getWalls() + self.startState = gameState.getPacmanPosition() + self.costFn = lambda x: 1 + self._visited, self._visitedlist, self._expanded = {}, [], 0 # DO NOT CHANGE + + def isGoalState(self, state: Tuple[int, int]): + """ + The state is Pacman's position. Fill this in with a goal test that will + complete the problem definition. + """ + x,y = state + + # 目标状态是Pacman到达任何食物的位置 + # 检查当前位置是否有食物 + return self.food[x][y] + +def mazeDistance(point1: Tuple[int, int], point2: Tuple[int, int], gameState: pacman.GameState) -> int: + """ + Returns the maze distance between any two points, using the search functions + you have already built. The gameState can be any game state -- Pacman's + position in that state is ignored. + + Example usage: mazeDistance( (2,4), (5,6), gameState) + + This might be a useful helper function for your ApproximateSearchAgent. + """ + x1, y1 = point1 + x2, y2 = point2 + walls = gameState.getWalls() + assert not walls[x1][y1], 'point1 is a wall: ' + str(point1) + assert not walls[x2][y2], 'point2 is a wall: ' + str(point2) + prob = PositionSearchProblem(gameState, start=point1, goal=point2, warn=False, visualize=False) + return len(search.bfs(prob)) diff --git a/proj1/searchTestClasses.py b/proj1/searchTestClasses.py new file mode 100755 index 0000000..f6b6866 --- /dev/null +++ b/proj1/searchTestClasses.py @@ -0,0 +1,823 @@ +# searchTestClasses.py +# -------------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +import sys +import re +import testClasses +import textwrap + +# import project specific code +import layout +import pacman +from search import SearchProblem + +# helper function for printing solutions in solution files +def wrap_solution(solution): + if type(solution) == type([]): + return '\n'.join(textwrap.wrap(' '.join(solution))) + else: + return str(solution) + + + + +def followAction(state, action, problem): + for successor1, action1, cost1 in problem.getSuccessors(state): + if action == action1: return successor1 + return None + +def followPath(path, problem): + state = problem.getStartState() + states = [state] + for action in path: + state = followAction(state, action, problem) + states.append(state) + return states + +def checkSolution(problem, path): + state = problem.getStartState() + for action in path: + state = followAction(state, action, problem) + return problem.isGoalState(state) + +# Search problem on a plain graph +class GraphSearch(SearchProblem): + + # Read in the state graph; define start/end states, edges and costs + def __init__(self, graph_text): + self.expanded_states = [] + lines = graph_text.split('\n') + r = re.match(r'start_state:(.*)', lines[0]) + if r == None: + print("Broken graph:") + print('"""%s"""' % graph_text) + raise Exception("GraphSearch graph specification start_state not found or incorrect on line 0") + self.start_state = r.group(1).strip() + r = re.match(r'goal_states:(.*)', lines[1]) + if r == None: + print("Broken graph:") + print('"""%s"""' % graph_text) + raise Exception("GraphSearch graph specification goal_states not found or incorrect on line 1") + goals = r.group(1).split() + self.goals = [str.strip(g) for g in goals] + self.successors = {} + all_states = set() + self.orderedSuccessorTuples = [] + for l in lines[2:]: + if len(l.split()) == 3: + start, action, next_state = l.split() + cost = 1 + elif len(l.split()) == 4: + start, action, next_state, cost = l.split() + else: + print("Broken graph:") + print('"""%s"""' % graph_text) + raise Exception("Invalid line in GraphSearch graph specification on line:" + l) + cost = float(cost) + self.orderedSuccessorTuples.append((start, action, next_state, cost)) + all_states.add(start) + all_states.add(next_state) + if start not in self.successors: + self.successors[start] = [] + self.successors[start].append((next_state, action, cost)) + for s in all_states: + if s not in self.successors: + self.successors[s] = [] + + # Get start state + def getStartState(self): + return self.start_state + + # Check if a state is a goal state + def isGoalState(self, state): + return state in self.goals + + # Get all successors of a state + def getSuccessors(self, state): + self.expanded_states.append(state) + return list(self.successors[state]) + + # Calculate total cost of a sequence of actions + def getCostOfActions(self, actions): + total_cost = 0 + state = self.start_state + for a in actions: + successors = self.successors[state] + match = False + for (next_state, action, cost) in successors: + if a == action: + state = next_state + total_cost += cost + match = True + if not match: + print('invalid action sequence') + sys.exit(1) + return total_cost + + # Return a list of all states on which 'getSuccessors' was called + def getExpandedStates(self): + return self.expanded_states + + def __str__(self): + print(self.successors) + edges = ["%s %s %s %s" % t for t in self.orderedSuccessorTuples] + return \ +"""start_state: %s +goal_states: %s +%s""" % (self.start_state, " ".join(self.goals), "\n".join(edges)) + + + +def parseHeuristic(heuristicText): + heuristic = {} + for line in heuristicText.split('\n'): + tokens = line.split() + if len(tokens) != 2: + print("Broken heuristic:") + print('"""%s"""' % heuristicText) + raise Exception("GraphSearch heuristic specification broken at tokens:" + str(tokens)) + state, h = tokens + heuristic[state] = float(h) + + def graphHeuristic(state, problem=None): + if state in heuristic: + return heuristic[state] + else: + import pprint + pp = pprint.PrettyPrinter(indent=4) + print("Heuristic:") + pp.pprint(heuristic) + raise Exception("Graph heuristic called with invalid state: " + str(state)) + + return graphHeuristic + + +class GraphSearchTest(testClasses.TestCase): + + def __init__(self, question, testDict): + super(GraphSearchTest, self).__init__(question, testDict) + self.graph_text = testDict['graph'] + self.alg = testDict['algorithm'] + self.diagram = testDict['diagram'] + self.exactExpansionOrder = testDict.get('exactExpansionOrder', 'True').lower() == "true" + if 'heuristic' in testDict: + self.heuristic = parseHeuristic(testDict['heuristic']) + else: + self.heuristic = None + + # Note that the return type of this function is a tripple: + # (solution, expanded states, error message) + def getSolInfo(self, search): + alg = getattr(search, self.alg) + problem = GraphSearch(self.graph_text) + if self.heuristic != None: + solution = alg(problem, self.heuristic) + else: + solution = alg(problem) + + if type(solution) != type([]): + return None, None, 'The result of %s must be a list. (Instead, it is %s)' % (self.alg, type(solution)) + + return solution, problem.getExpandedStates(), None + + # Run student code. If an error message is returned, print error and return false. + # If a good solution is returned, printn the solution and return true; otherwise, + # print both the correct and student's solution and return false. + def execute(self, grades, moduleDict, solutionDict): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + gold_solution = [str.split(solutionDict['solution']), str.split(solutionDict['rev_solution'])] + gold_expanded_states = [str.split(solutionDict['expanded_states']), str.split(solutionDict['rev_expanded_states'])] + + solution, expanded_states, error = self.getSolInfo(search) + if error != None: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('\t%s' % error) + return False + + if solution in gold_solution and (not self.exactExpansionOrder or expanded_states in gold_expanded_states): + grades.addMessage('PASS: %s' % self.path) + grades.addMessage('\tsolution:\t\t%s' % solution) + grades.addMessage('\texpanded_states:\t%s' % expanded_states) + return True + else: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('\tgraph:') + for line in self.diagram.split('\n'): + grades.addMessage('\t %s' % (line,)) + grades.addMessage('\tstudent solution:\t\t%s' % solution) + grades.addMessage('\tstudent expanded_states:\t%s' % expanded_states) + grades.addMessage('') + grades.addMessage('\tcorrect solution:\t\t%s' % gold_solution[0]) + grades.addMessage('\tcorrect expanded_states:\t%s' % gold_expanded_states[0]) + grades.addMessage('\tcorrect rev_solution:\t\t%s' % gold_solution[1]) + grades.addMessage('\tcorrect rev_expanded_states:\t%s' % gold_expanded_states[1]) + return False + + def writeSolution(self, moduleDict, filePath): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + # open file and write comments + handle = open(filePath, 'w') + handle.write('# This is the solution file for %s.\n' % self.path) + handle.write('# This solution is designed to support both right-to-left\n') + handle.write('# and left-to-right implementations.\n') + + # write forward solution + solution, expanded_states, error = self.getSolInfo(search) + if error != None: raise Exception("Error in solution code: %s" % error) + handle.write('solution: "%s"\n' % ' '.join(solution)) + handle.write('expanded_states: "%s"\n' % ' '.join(expanded_states)) + + # reverse and write backwards solution + search.REVERSE_PUSH = not search.REVERSE_PUSH + solution, expanded_states, error = self.getSolInfo(search) + if error != None: raise Exception("Error in solution code: %s" % error) + handle.write('rev_solution: "%s"\n' % ' '.join(solution)) + handle.write('rev_expanded_states: "%s"\n' % ' '.join(expanded_states)) + + # clean up + search.REVERSE_PUSH = not search.REVERSE_PUSH + handle.close() + return True + + + +class PacmanSearchTest(testClasses.TestCase): + + def __init__(self, question, testDict): + super(PacmanSearchTest, self).__init__(question, testDict) + self.layout_text = testDict['layout'] + self.alg = testDict['algorithm'] + self.layoutName = testDict['layoutName'] + + # TODO: sensible to have defaults like this? + self.leewayFactor = float(testDict.get('leewayFactor', '1')) + self.costFn = eval(testDict.get('costFn', 'None')) + self.searchProblemClassName = testDict.get('searchProblemClass', 'PositionSearchProblem') + self.heuristicName = testDict.get('heuristic', None) + + + def getSolInfo(self, search, searchAgents): + alg = getattr(search, self.alg) + lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')]) + start_state = pacman.GameState() + start_state.initialize(lay, 0) + + problemClass = getattr(searchAgents, self.searchProblemClassName) + problemOptions = {} + if self.costFn != None: + problemOptions['costFn'] = self.costFn + problem = problemClass(start_state, **problemOptions) + heuristic = getattr(searchAgents, self.heuristicName) if self.heuristicName != None else None + + if heuristic != None: + solution = alg(problem, heuristic) + else: + solution = alg(problem) + + if type(solution) != type([]): + return None, None, 'The result of %s must be a list. (Instead, it is %s)' % (self.alg, type(solution)) + + from game import Directions + dirs = Directions.LEFT.keys() + if [el in dirs for el in solution].count(False) != 0: + return None, None, 'Output of %s must be a list of actions from game.Directions' % self.alg + + expanded = problem._expanded + return solution, expanded, None + + def execute(self, grades, moduleDict, solutionDict): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + gold_solution = [str.split(solutionDict['solution']), str.split(solutionDict['rev_solution'])] + gold_expanded = max(int(solutionDict['expanded_nodes']), int(solutionDict['rev_expanded_nodes'])) + + solution, expanded, error = self.getSolInfo(search, searchAgents) + if error != None: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('%s' % error) + return False + + # FIXME: do we want to standardize test output format? + + if solution not in gold_solution: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('Solution not correct.') + grades.addMessage('\tstudent solution length: %s' % len(solution)) + grades.addMessage('\tstudent solution:\n%s' % wrap_solution(solution)) + grades.addMessage('') + grades.addMessage('\tcorrect solution length: %s' % len(gold_solution[0])) + grades.addMessage('\tcorrect (reversed) solution length: %s' % len(gold_solution[1])) + grades.addMessage('\tcorrect solution:\n%s' % wrap_solution(gold_solution[0])) + grades.addMessage('\tcorrect (reversed) solution:\n%s' % wrap_solution(gold_solution[1])) + return False + + if expanded > self.leewayFactor * gold_expanded and expanded > gold_expanded + 1: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('Too many node expanded; are you expanding nodes twice?') + grades.addMessage('\tstudent nodes expanded: %s' % expanded) + grades.addMessage('') + grades.addMessage('\tcorrect nodes expanded: %s (leewayFactor %s)' % (gold_expanded, self.leewayFactor)) + return False + + grades.addMessage('PASS: %s' % self.path) + grades.addMessage('\tpacman layout:\t\t%s' % self.layoutName) + grades.addMessage('\tsolution length: %s' % len(solution)) + grades.addMessage('\tnodes expanded:\t\t%s' % expanded) + return True + + + def writeSolution(self, moduleDict, filePath): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + # open file and write comments + handle = open(filePath, 'w') + handle.write('# This is the solution file for %s.\n' % self.path) + handle.write('# This solution is designed to support both right-to-left\n') + handle.write('# and left-to-right implementations.\n') + handle.write('# Number of nodes expanded must be with a factor of %s of the numbers below.\n' % self.leewayFactor) + + # write forward solution + solution, expanded, error = self.getSolInfo(search, searchAgents) + if error != None: raise Exception("Error in solution code: %s" % error) + handle.write('solution: """\n%s\n"""\n' % wrap_solution(solution)) + handle.write('expanded_nodes: "%s"\n' % expanded) + + # write backward solution + search.REVERSE_PUSH = not search.REVERSE_PUSH + solution, expanded, error = self.getSolInfo(search, searchAgents) + if error != None: raise Exception("Error in solution code: %s" % error) + handle.write('rev_solution: """\n%s\n"""\n' % wrap_solution(solution)) + handle.write('rev_expanded_nodes: "%s"\n' % expanded) + + # clean up + search.REVERSE_PUSH = not search.REVERSE_PUSH + handle.close() + return True + + +from game import Actions +def getStatesFromPath(start, path): + "Returns the list of states visited along the path" + vis = [start] + curr = start + for a in path: + x,y = curr + dx, dy = Actions.directionToVector(a) + curr = (int(x + dx), int(y + dy)) + vis.append(curr) + return vis + +class CornerProblemTest(testClasses.TestCase): + + def __init__(self, question, testDict): + super(CornerProblemTest, self).__init__(question, testDict) + self.layoutText = testDict['layout'] + self.layoutName = testDict['layoutName'] + + def solution(self, search, searchAgents): + lay = layout.Layout([l.strip() for l in self.layoutText.split('\n')]) + gameState = pacman.GameState() + gameState.initialize(lay, 0) + problem = searchAgents.CornersProblem(gameState) + path = search.bfs(problem) + + gameState = pacman.GameState() + gameState.initialize(lay, 0) + visited = getStatesFromPath(gameState.getPacmanPosition(), path) + top, right = gameState.getWalls().height-2, gameState.getWalls().width-2 + missedCorners = [p for p in ((1,1), (1,top), (right, 1), (right, top)) if p not in visited] + + return path, missedCorners + + def execute(self, grades, moduleDict, solutionDict): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + gold_length = int(solutionDict['solution_length']) + solution, missedCorners = self.solution(search, searchAgents) + + if type(solution) != type([]): + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('The result must be a list. (Instead, it is %s)' % type(solution)) + return False + + if len(missedCorners) != 0: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('Corners missed: %s' % missedCorners) + return False + + if len(solution) != gold_length: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('Optimal solution not found.') + grades.addMessage('\tstudent solution length:\n%s' % len(solution)) + grades.addMessage('') + grades.addMessage('\tcorrect solution length:\n%s' % gold_length) + return False + + grades.addMessage('PASS: %s' % self.path) + grades.addMessage('\tpacman layout:\t\t%s' % self.layoutName) + grades.addMessage('\tsolution length:\t\t%s' % len(solution)) + return True + + def writeSolution(self, moduleDict, filePath): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + # open file and write comments + handle = open(filePath, 'w') + handle.write('# This is the solution file for %s.\n' % self.path) + + print("Solving problem", self.layoutName) + print(self.layoutText) + + path, _ = self.solution(search, searchAgents) + length = len(path) + print("Problem solved") + + handle.write('solution_length: "%s"\n' % length) + handle.close() + + + + +# template = """class: "HeuristicTest" +# +# heuristic: "foodHeuristic" +# searchProblemClass: "FoodSearchProblem" +# layoutName: "Test %s" +# layout: \"\"\" +# %s +# \"\"\" +# """ +# +# for i, (_, _, l) in enumerate(doneTests + foodTests): +# f = open("food_heuristic_%s.test" % (i+1), "w") +# f.write(template % (i+1, "\n".join(l))) +# f.close() + +class HeuristicTest(testClasses.TestCase): + + def __init__(self, question, testDict): + super(HeuristicTest, self).__init__(question, testDict) + self.layoutText = testDict['layout'] + self.layoutName = testDict['layoutName'] + self.searchProblemClassName = testDict['searchProblemClass'] + self.heuristicName = testDict['heuristic'] + + def setupProblem(self, searchAgents): + lay = layout.Layout([l.strip() for l in self.layoutText.split('\n')]) + gameState = pacman.GameState() + gameState.initialize(lay, 0) + problemClass = getattr(searchAgents, self.searchProblemClassName) + problem = problemClass(gameState) + state = problem.getStartState() + heuristic = getattr(searchAgents, self.heuristicName) + + return problem, state, heuristic + + def checkHeuristic(self, heuristic, problem, state, solutionCost): + h0 = heuristic(state, problem) + + if solutionCost == 0: + if h0 == 0: + return True, '' + else: + return False, 'Heuristic failed H(goal) == 0 test' + + if h0 < 0: + return False, 'Heuristic failed H >= 0 test' + if not h0 > 0: + return False, 'Heuristic failed non-triviality test' + if not h0 <= solutionCost: + return False, 'Heuristic failed admissibility test' + + for succ, action, stepCost in problem.getSuccessors(state): + h1 = heuristic(succ, problem) + if h1 < 0: return False, 'Heuristic failed H >= 0 test' + if h0 - h1 > stepCost: return False, 'Heuristic failed consistency test' + + return True, '' + + def execute(self, grades, moduleDict, solutionDict): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + solutionCost = int(solutionDict['solution_cost']) + problem, state, heuristic = self.setupProblem(searchAgents) + + passed, message = self.checkHeuristic(heuristic, problem, state, solutionCost) + + if not passed: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('%s' % message) + return False + else: + grades.addMessage('PASS: %s' % self.path) + return True + + def writeSolution(self, moduleDict, filePath): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + # open file and write comments + handle = open(filePath, 'w') + handle.write('# This is the solution file for %s.\n' % self.path) + + print("Solving problem", self.layoutName, self.heuristicName) + print(self.layoutText) + problem, _, heuristic = self.setupProblem(searchAgents) + path = search.astar(problem, heuristic) + cost = problem.getCostOfActions(path) + print("Problem solved") + + handle.write('solution_cost: "%s"\n' % cost) + handle.close() + return True + + + + + + +class HeuristicGrade(testClasses.TestCase): + + def __init__(self, question, testDict): + super(HeuristicGrade, self).__init__(question, testDict) + self.layoutText = testDict['layout'] + self.layoutName = testDict['layoutName'] + self.searchProblemClassName = testDict['searchProblemClass'] + self.heuristicName = testDict['heuristic'] + self.basePoints = int(testDict['basePoints']) + self.thresholds = [int(t) for t in testDict['gradingThresholds'].split()] + + def setupProblem(self, searchAgents): + lay = layout.Layout([l.strip() for l in self.layoutText.split('\n')]) + gameState = pacman.GameState() + gameState.initialize(lay, 0) + problemClass = getattr(searchAgents, self.searchProblemClassName) + problem = problemClass(gameState) + state = problem.getStartState() + heuristic = getattr(searchAgents, self.heuristicName) + + return problem, state, heuristic + + + def execute(self, grades, moduleDict, solutionDict): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + problem, _, heuristic = self.setupProblem(searchAgents) + + path = search.astar(problem, heuristic) + + expanded = problem._expanded + + if not checkSolution(problem, path): + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('\tReturned path is not a solution.') + grades.addMessage('\tpath returned by astar: %s' % expanded) + return False + + grades.addPoints(self.basePoints) + points = 0 + for threshold in self.thresholds: + if expanded <= threshold: + points += 1 + grades.addPoints(points) + if points >= len(self.thresholds): + grades.addMessage('PASS: %s' % self.path) + else: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('\texpanded nodes: %s' % expanded) + grades.addMessage('\tthresholds: %s' % self.thresholds) + + return True + + + def writeSolution(self, moduleDict, filePath): + handle = open(filePath, 'w') + handle.write('# This is the solution file for %s.\n' % self.path) + handle.write('# File intentionally blank.\n') + handle.close() + return True + + + + + +# template = """class: "ClosestDotTest" +# +# layoutName: "Test %s" +# layout: \"\"\" +# %s +# \"\"\" +# """ +# +# for i, (_, _, l) in enumerate(foodTests): +# f = open("closest_dot_%s.test" % (i+1), "w") +# f.write(template % (i+1, "\n".join(l))) +# f.close() + +class ClosestDotTest(testClasses.TestCase): + + def __init__(self, question, testDict): + super(ClosestDotTest, self).__init__(question, testDict) + self.layoutText = testDict['layout'] + self.layoutName = testDict['layoutName'] + + def solution(self, searchAgents): + lay = layout.Layout([l.strip() for l in self.layoutText.split('\n')]) + gameState = pacman.GameState() + gameState.initialize(lay, 0) + path = searchAgents.ClosestDotSearchAgent().findPathToClosestDot(gameState) + return path + + def execute(self, grades, moduleDict, solutionDict): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + gold_length = int(solutionDict['solution_length']) + solution = self.solution(searchAgents) + + if type(solution) != type([]): + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('\tThe result must be a list. (Instead, it is %s)' % type(solution)) + return False + + if len(solution) != gold_length: + grades.addMessage('FAIL: %s' % self.path) + grades.addMessage('Closest dot not found.') + grades.addMessage('\tstudent solution length:\n%s' % len(solution)) + grades.addMessage('') + grades.addMessage('\tcorrect solution length:\n%s' % gold_length) + return False + + grades.addMessage('PASS: %s' % self.path) + grades.addMessage('\tpacman layout:\t\t%s' % self.layoutName) + grades.addMessage('\tsolution length:\t\t%s' % len(solution)) + return True + + def writeSolution(self, moduleDict, filePath): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + # open file and write comments + handle = open(filePath, 'w') + handle.write('# This is the solution file for %s.\n' % self.path) + + print("Solving problem", self.layoutName) + print(self.layoutText) + + length = len(self.solution(searchAgents)) + print("Problem solved") + + handle.write('solution_length: "%s"\n' % length) + handle.close() + return True + + + + +class CornerHeuristicSanity(testClasses.TestCase): + + def __init__(self, question, testDict): + super(CornerHeuristicSanity, self).__init__(question, testDict) + self.layout_text = testDict['layout'] + + def execute(self, grades, moduleDict, solutionDict): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + game_state = pacman.GameState() + lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')]) + game_state.initialize(lay, 0) + problem = searchAgents.CornersProblem(game_state) + start_state = problem.getStartState() + h0 = searchAgents.cornersHeuristic(start_state, problem) + succs = problem.getSuccessors(start_state) + # cornerConsistencyA + for succ in succs: + h1 = searchAgents.cornersHeuristic(succ[0], problem) + if h0 - h1 > 1: + grades.addMessage('FAIL: inconsistent heuristic') + return False + heuristic_cost = searchAgents.cornersHeuristic(start_state, problem) + true_cost = float(solutionDict['cost']) + # cornerNontrivial + if heuristic_cost == 0: + grades.addMessage('FAIL: must use non-trivial heuristic') + return False + # cornerAdmissible + if heuristic_cost > true_cost: + grades.addMessage('FAIL: Inadmissible heuristic') + return False + path = solutionDict['path'].split() + states = followPath(path, problem) + heuristics = [] + for state in states: + heuristics.append(searchAgents.cornersHeuristic(state, problem)) + for i in range(0, len(heuristics) - 1): + h0 = heuristics[i] + h1 = heuristics[i+1] + # cornerConsistencyB + if h0 - h1 > 1: + grades.addMessage('FAIL: inconsistent heuristic') + return False + # cornerPosH + if h0 < 0 or h1 <0: + grades.addMessage('FAIL: non-positive heuristic') + return False + # cornerGoalH + if heuristics[len(heuristics) - 1] != 0: + grades.addMessage('FAIL: heuristic non-zero at goal') + return False + grades.addMessage('PASS: heuristic value less than true cost at start state') + return True + + def writeSolution(self, moduleDict, filePath): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + # write comment + handle = open(filePath, 'w') + handle.write('# In order for a heuristic to be admissible, the value\n') + handle.write('# of the heuristic must be less at each state than the\n') + handle.write('# true cost of the optimal path from that state to a goal.\n') + + # solve problem and write solution + lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')]) + start_state = pacman.GameState() + start_state.initialize(lay, 0) + problem = searchAgents.CornersProblem(start_state) + solution = search.astar(problem, searchAgents.cornersHeuristic) + handle.write('cost: "%d"\n' % len(solution)) + handle.write('path: """\n%s\n"""\n' % wrap_solution(solution)) + handle.close() + return True + + + +class CornerHeuristicPacman(testClasses.TestCase): + + def __init__(self, question, testDict): + super(CornerHeuristicPacman, self).__init__(question, testDict) + self.layout_text = testDict['layout'] + + def execute(self, grades, moduleDict, solutionDict): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + total = 0 + true_cost = float(solutionDict['cost']) + thresholds = [int(x) for x in solutionDict['thresholds'].split()] + game_state = pacman.GameState() + lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')]) + game_state.initialize(lay, 0) + problem = searchAgents.CornersProblem(game_state) + start_state = problem.getStartState() + if searchAgents.cornersHeuristic(start_state, problem) > true_cost: + grades.addMessage('FAIL: Inadmissible heuristic') + return False + path = search.astar(problem, searchAgents.cornersHeuristic) + print("path:", path) + print("path length:", len(path)) + cost = problem.getCostOfActions(path) + if cost > true_cost: + grades.addMessage('FAIL: Inconsistent heuristic') + return False + expanded = problem._expanded + points = 0 + for threshold in thresholds: + if expanded <= threshold: + points += 1 + grades.addPoints(points) + if points >= len(thresholds): + grades.addMessage('PASS: Heuristic resulted in expansion of %d nodes' % expanded) + else: + grades.addMessage('FAIL: Heuristic resulted in expansion of %d nodes' % expanded) + return True + + def writeSolution(self, moduleDict, filePath): + search = moduleDict['search'] + searchAgents = moduleDict['searchAgents'] + # write comment + handle = open(filePath, 'w') + handle.write('# This solution file specifies the length of the optimal path\n') + handle.write('# as well as the thresholds on number of nodes expanded to be\n') + handle.write('# used in scoring.\n') + + # solve problem and write solution + lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')]) + start_state = pacman.GameState() + start_state.initialize(lay, 0) + problem = searchAgents.CornersProblem(start_state) + solution = search.astar(problem, searchAgents.cornersHeuristic) + handle.write('cost: "%d"\n' % len(solution)) + handle.write('path: """\n%s\n"""\n' % wrap_solution(solution)) + handle.write('thresholds: "2000 1600 1200"\n') + handle.close() + return True + diff --git a/proj1/testClasses.py b/proj1/testClasses.py new file mode 100755 index 0000000..bf60a71 --- /dev/null +++ b/proj1/testClasses.py @@ -0,0 +1,210 @@ +# testClasses.py +# -------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +# import modules from python standard library +import inspect +import re +import sys + + +# Class which models a question in a project. Note that questions have a +# maximum number of points they are worth, and are composed of a series of +# test cases +class Question(object): + + def raiseNotDefined(self): + print('Method not implemented: %s' % inspect.stack()[1][3]) + sys.exit(1) + + def __init__(self, questionDict, display, timeout=None): + self.maxPoints = int(questionDict['max_points']) + self.testCases = [] + self.display = display + self.timeout = int(questionDict["timeout"]) if "timeout" in questionDict else None + + def getDisplay(self): + return self.display + + def getMaxPoints(self): + return self.maxPoints + + def getTimeout(self): + return self.timeout + + # Note that 'thunk' must be a function which accepts a single argument, + # namely a 'grading' object + def addTestCase(self, testCase, thunk): + self.testCases.append((testCase, thunk)) + + def execute(self, grades): + self.raiseNotDefined() + +# Question in which all test cases must be passed in order to receive credit +class PassAllTestsQuestion(Question): + + def execute(self, grades): + # TODO: is this the right way to use grades? The autograder doesn't seem to use it. + testsFailed = False + grades.assignZeroCredit() + for _, f in self.testCases: + if not f(grades): + testsFailed = True + if testsFailed: + grades.fail("Tests failed.") + else: + grades.assignFullCredit() + +class ExtraCreditPassAllTestsQuestion(Question): + def __init__(self, questionDict, display): + Question.__init__(self, questionDict, display) + self.extraPoints = int(questionDict['extra_points']) + + def execute(self, grades): + # TODO: is this the right way to use grades? The autograder doesn't seem to use it. + testsFailed = False + grades.assignZeroCredit() + for _, f in self.testCases: + if not f(grades): + testsFailed = True + if testsFailed: + grades.fail("Tests failed.") + else: + grades.assignFullCredit() + grades.addPoints(self.extraPoints) + +# Question in which predict credit is given for test cases with a ``points'' property. +# All other tests are mandatory and must be passed. +class HackedPartialCreditQuestion(Question): + + def execute(self, grades): + # TODO: is this the right way to use grades? The autograder doesn't seem to use it. + grades.assignZeroCredit() + + points = 0 + passed = True + for testCase, f in self.testCases: + testResult = f(grades) + if "points" in testCase.testDict: + if testResult: points += float(testCase.testDict["points"]) + else: + passed = passed and testResult + + ## FIXME: Below terrible hack to match q3's logic + if int(points) == self.maxPoints and not passed: + grades.assignZeroCredit() + else: + grades.addPoints(int(points)) + + +class Q6PartialCreditQuestion(Question): + """Fails any test which returns False, otherwise doesn't effect the grades object. + Partial credit tests will add the required points.""" + + def execute(self, grades): + grades.assignZeroCredit() + + results = [] + for _, f in self.testCases: + results.append(f(grades)) + if False in results: + grades.assignZeroCredit() + +class PartialCreditQuestion(Question): + """Fails any test which returns False, otherwise doesn't effect the grades object. + Partial credit tests will add the required points.""" + + def execute(self, grades): + grades.assignZeroCredit() + + for _, f in self.testCases: + if not f(grades): + grades.assignZeroCredit() + grades.fail("Tests failed.") + return False + + + +class NumberPassedQuestion(Question): + """Grade is the number of test cases passed.""" + + def execute(self, grades): + grades.addPoints([f(grades) for _, f in self.testCases].count(True)) + + + + + +# Template modeling a generic test case +class TestCase(object): + + def raiseNotDefined(self): + print('Method not implemented: %s' % inspect.stack()[1][3]) + sys.exit(1) + + def getPath(self): + return self.path + + def __init__(self, question, testDict): + self.question = question + self.testDict = testDict + self.path = testDict['path'] + self.messages = [] + + def __str__(self): + self.raiseNotDefined() + + def execute(self, grades, moduleDict, solutionDict): + self.raiseNotDefined() + + def writeSolution(self, moduleDict, filePath): + self.raiseNotDefined() + return True + + # Tests should call the following messages for grading + # to ensure a uniform format for test output. + # + # TODO: this is hairy, but we need to fix grading.py's interface + # to get a nice hierarchical project - question - test structure, + # then these should be moved into Question proper. + def testPass(self, grades): + grades.addMessage('PASS: %s' % (self.path,)) + for line in self.messages: + grades.addMessage(' %s' % (line,)) + return True + + def testFail(self, grades): + grades.addMessage('FAIL: %s' % (self.path,)) + for line in self.messages: + grades.addMessage(' %s' % (line,)) + return False + + # This should really be question level? + # + def testPartial(self, grades, points, maxPoints): + grades.addPoints(points) + extraCredit = max(0, points - maxPoints) + regularCredit = points - extraCredit + + grades.addMessage('%s: %s (%s of %s points)' % ("PASS" if points >= maxPoints else "FAIL", self.path, regularCredit, maxPoints)) + if extraCredit > 0: + grades.addMessage('EXTRA CREDIT: %s points' % (extraCredit,)) + + for line in self.messages: + grades.addMessage(' %s' % (line,)) + + return True + + def addMessage(self, message): + self.messages.extend(message.split('\n')) + diff --git a/proj1/testParser.py b/proj1/testParser.py new file mode 100755 index 0000000..8f5dd2f --- /dev/null +++ b/proj1/testParser.py @@ -0,0 +1,85 @@ +# testParser.py +# ------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +import re +import sys + +class TestParser(object): + + def __init__(self, path): + # save the path to the test file + self.path = path + + def removeComments(self, rawlines): + # remove any portion of a line following a '#' symbol + fixed_lines = [] + for l in rawlines: + idx = l.find('#') + if idx == -1: + fixed_lines.append(l) + else: + fixed_lines.append(l[0:idx]) + return '\n'.join(fixed_lines) + + def parse(self): + # read in the test case and remove comments + test = {} + with open(self.path) as handle: + raw_lines = handle.read().split('\n') + + test_text = self.removeComments(raw_lines) + test['__raw_lines__'] = raw_lines + test['path'] = self.path + test['__emit__'] = [] + lines = test_text.split('\n') + i = 0 + # read a property in each loop cycle + while(i < len(lines)): + # skip blank lines + if re.match(r'\A\s*\Z', lines[i]): + test['__emit__'].append(("raw", raw_lines[i])) + i += 1 + continue + m = re.match(r'\A([^"]*?):\s*"([^"]*)"\s*\Z', lines[i]) + if m: + test[m.group(1)] = m.group(2) + test['__emit__'].append(("oneline", m.group(1))) + i += 1 + continue + m = re.match(r'\A([^"]*?):\s*"""\s*\Z', lines[i]) + if m: + msg = [] + i += 1 + while(not re.match(r'\A\s*"""\s*\Z', lines[i])): + msg.append(raw_lines[i]) + i += 1 + test[m.group(1)] = '\n'.join(msg) + test['__emit__'].append(("multiline", m.group(1))) + i += 1 + continue + print('error parsing test file: %s' % self.path) + sys.exit(1) + return test + + +def emitTestDict(testDict, handle): + for kind, data in testDict['__emit__']: + if kind == "raw": + handle.write(data + "\n") + elif kind == "oneline": + handle.write('%s: "%s"\n' % (data, testDict[data])) + elif kind == "multiline": + handle.write('%s: """\n%s\n"""\n' % (data, testDict[data])) + else: + raise Exception("Bad __emit__") diff --git a/proj1/test_cases/CONFIG b/proj1/test_cases/CONFIG new file mode 100755 index 0000000..dbed66b --- /dev/null +++ b/proj1/test_cases/CONFIG @@ -0,0 +1 @@ +order: "q1 q2 q3 q4 q5 q6 q7 q8" \ No newline at end of file diff --git a/proj1/test_cases/q1/CONFIG b/proj1/test_cases/q1/CONFIG new file mode 100755 index 0000000..720cf94 --- /dev/null +++ b/proj1/test_cases/q1/CONFIG @@ -0,0 +1,3 @@ +max_points: "3" +class: "PassAllTestsQuestion" +timeout: "15" diff --git a/proj1/test_cases/q1/graph_backtrack.solution b/proj1/test_cases/q1/graph_backtrack.solution new file mode 100755 index 0000000..c52850c --- /dev/null +++ b/proj1/test_cases/q1/graph_backtrack.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q1/graph_backtrack.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->C 0:C->G" +expanded_states: "A D C" +rev_solution: "1:A->C 0:C->G" +rev_expanded_states: "A B C" diff --git a/proj1/test_cases/q1/graph_backtrack.test b/proj1/test_cases/q1/graph_backtrack.test new file mode 100755 index 0000000..05640a0 --- /dev/null +++ b/proj1/test_cases/q1/graph_backtrack.test @@ -0,0 +1,32 @@ +class: "GraphSearchTest" +algorithm: "depthFirstSearch" + +diagram: """ + B + ^ + | +*A --> C --> G + | + V + D + +A is the start state, G is the goal. Arrows mark +possible state transitions. This tests whether +you extract the sequence of actions correctly even +if your search backtracks. If you fail this, your +nodes are not correctly tracking the sequences of +actions required to reach them. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +A 1:A->C C 2.0 +A 2:A->D D 4.0 +C 0:C->G G 8.0 +""" diff --git a/proj1/test_cases/q1/graph_bfs_vs_dfs.solution b/proj1/test_cases/q1/graph_bfs_vs_dfs.solution new file mode 100755 index 0000000..0680f92 --- /dev/null +++ b/proj1/test_cases/q1/graph_bfs_vs_dfs.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q1/graph_bfs_vs_dfs.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "2:A->D 0:D->G" +expanded_states: "A D" +rev_solution: "0:A->B 0:B->D 0:D->G" +rev_expanded_states: "A B D" diff --git a/proj1/test_cases/q1/graph_bfs_vs_dfs.test b/proj1/test_cases/q1/graph_bfs_vs_dfs.test new file mode 100755 index 0000000..155e1fe --- /dev/null +++ b/proj1/test_cases/q1/graph_bfs_vs_dfs.test @@ -0,0 +1,30 @@ +# Graph where BFS finds the optimal solution but DFS does not +class: "GraphSearchTest" +algorithm: "depthFirstSearch" + +diagram: """ +/-- B +| ^ +| | +| *A -->[G] +| | ^ +| V | +\-->D ----/ + +A is the start state, G is the goal. Arrows +mark possible transitions +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +A 1:A->G G 2.0 +A 2:A->D D 4.0 +B 0:B->D D 8.0 +D 0:D->G G 16.0 +""" diff --git a/proj1/test_cases/q1/graph_infinite.solution b/proj1/test_cases/q1/graph_infinite.solution new file mode 100755 index 0000000..82203ee --- /dev/null +++ b/proj1/test_cases/q1/graph_infinite.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q1/graph_infinite.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "0:A->B 1:B->C 1:C->G" +expanded_states: "A B C" +rev_solution: "0:A->B 1:B->C 1:C->G" +rev_expanded_states: "A B C" diff --git a/proj1/test_cases/q1/graph_infinite.test b/proj1/test_cases/q1/graph_infinite.test new file mode 100755 index 0000000..692ac05 --- /dev/null +++ b/proj1/test_cases/q1/graph_infinite.test @@ -0,0 +1,30 @@ +# Graph where natural action choice leads to an infinite loop +class: "GraphSearchTest" +algorithm: "depthFirstSearch" + +diagram: """ + B <--> C + ^ /| + | / | + V / V +*A<-/ [G] + +A is the start state, G is the goal. Arrows mark +possible state transitions. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +B 0:B->A A 2.0 +B 1:B->C C 4.0 +C 0:C->A A 8.0 +C 1:C->G G 16.0 +C 2:C->B B 32.0 +""" + diff --git a/proj1/test_cases/q1/graph_manypaths.solution b/proj1/test_cases/q1/graph_manypaths.solution new file mode 100755 index 0000000..34b5a82 --- /dev/null +++ b/proj1/test_cases/q1/graph_manypaths.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q1/graph_manypaths.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "2:A->B2 0:B2->C 0:C->D 2:D->E2 0:E2->F 0:F->G" +expanded_states: "A B2 C D E2 F" +rev_solution: "0:A->B1 0:B1->C 0:C->D 0:D->E1 0:E1->F 0:F->G" +rev_expanded_states: "A B1 C D E1 F" diff --git a/proj1/test_cases/q1/graph_manypaths.test b/proj1/test_cases/q1/graph_manypaths.test new file mode 100755 index 0000000..953c4eb --- /dev/null +++ b/proj1/test_cases/q1/graph_manypaths.test @@ -0,0 +1,39 @@ +class: "GraphSearchTest" +algorithm: "depthFirstSearch" + +diagram: """ + B1 E1 + ^ \ ^ \ + / V / V +*A --> C --> D --> F --> [G] + \ ^ \ ^ + V / V / + B2 E2 + +A is the start state, G is the goal. Arrows mark +possible state transitions. This graph has multiple +paths to the goal, where nodes with the same state +are added to the fringe multiple times before they +are expanded. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B1 B1 1.0 +A 1:A->C C 2.0 +A 2:A->B2 B2 4.0 +B1 0:B1->C C 8.0 +B2 0:B2->C C 16.0 +C 0:C->D D 32.0 +D 0:D->E1 E1 64.0 +D 1:D->F F 128.0 +D 2:D->E2 E2 256.0 +E1 0:E1->F F 512.0 +E2 0:E2->F F 1024.0 +F 0:F->G G 2048.0 +""" diff --git a/proj1/test_cases/q1/pacman_1.solution b/proj1/test_cases/q1/pacman_1.solution new file mode 100755 index 0000000..82a670c --- /dev/null +++ b/proj1/test_cases/q1/pacman_1.solution @@ -0,0 +1,40 @@ +# This is the solution file for test_cases/q1/pacman_1.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +# Number of nodes expanded must be with a factor of 1.0 of the numbers below. +solution: """ +West West West West West West West West West West West West West West +West West West West West West West West West West West West West West +West West West West West South South South South South South South +South South East East East North North North North North North North +East East South South South South South South East East North North +North North North North East East South South South South East East +North North East East East East East East East East South South South +East East East East East East East South South South South South South +South West West West West West West West West West West West West West +West West West West South West West West West West West West West West +""" +expanded_nodes: "146" +rev_solution: """ +South South West West West West South South East East East East South +South West West West West South South East East East East South South +West West West West South South South East North East East East South +South South West West West West West West West North North North North +North North North North West West West West West West West North North +North East East East East South East East East North North North West +West North North West West West West West West West West West West +West West West West West West West West West West West West West West +South South South South South South South South South East East East +North North North North North North North East East South South South +South South South East East North North North North North North East +East South South South South East East North North North North East +East East East East South South West West West South South East East +East South South West West West West West West South South West West +West West West South West West West West West South South East East +East East East East East North East East East East East North North +East East East East East East North East East East East East South +South West West West South West West West West West West South South +West West West West West South West West West West West West West West +West +""" +rev_expanded_nodes: "269" diff --git a/proj1/test_cases/q1/pacman_1.test b/proj1/test_cases/q1/pacman_1.test new file mode 100755 index 0000000..6ae5412 --- /dev/null +++ b/proj1/test_cases/q1/pacman_1.test @@ -0,0 +1,27 @@ +# This is a basic depth first search test +class: "PacmanSearchTest" +algorithm: "depthFirstSearch" + +# The following specifies the layout to be used +layoutName: "mediumMaze" +layout: """ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% % +% %% % % %%%%%%% %% % +% %% % % % % %%%% %%%%%%%%% %% %%%%% +% %% % % % % %% %% % +% %% % % % % % %%%% %%% %%%%%% % +% % % % % % %% %%%%%%%% % +% %% % % %%%%%%%% %% %% %%%%% +% %% % %% %%%%%%%%% %% % +% %%%%%% %%%%%%% %% %%%%%% % +%%%%%% % %%%% %% % % +% %%%%%% %%%%% % %% %% %%%%% +% %%%%%% % %%%%% %% % +% %%%%%% %%%%%%%%%%% %% %% % +%%%%%%%%%% %%%%%% % +%. %%%%%%%%%%%%%%%% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +""" + diff --git a/proj1/test_cases/q2/CONFIG b/proj1/test_cases/q2/CONFIG new file mode 100755 index 0000000..d105958 --- /dev/null +++ b/proj1/test_cases/q2/CONFIG @@ -0,0 +1,3 @@ +max_points: "3" +class: "PassAllTestsQuestion" +timeout: "30" diff --git a/proj1/test_cases/q2/graph_backtrack.solution b/proj1/test_cases/q2/graph_backtrack.solution new file mode 100755 index 0000000..6c669c2 --- /dev/null +++ b/proj1/test_cases/q2/graph_backtrack.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q2/graph_backtrack.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->C 0:C->G" +expanded_states: "A B C D" +rev_solution: "1:A->C 0:C->G" +rev_expanded_states: "A D C B" diff --git a/proj1/test_cases/q2/graph_backtrack.test b/proj1/test_cases/q2/graph_backtrack.test new file mode 100755 index 0000000..2b35d8b --- /dev/null +++ b/proj1/test_cases/q2/graph_backtrack.test @@ -0,0 +1,32 @@ +class: "GraphSearchTest" +algorithm: "breadthFirstSearch" + +diagram: """ + B + ^ + | +*A --> C --> G + | + V + D + +A is the start state, G is the goal. Arrows mark +possible state transitions. This tests whether +you extract the sequence of actions correctly even +if your search backtracks. If you fail this, your +nodes are not correctly tracking the sequences of +actions required to reach them. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +A 1:A->C C 2.0 +A 2:A->D D 4.0 +C 0:C->G G 8.0 +""" diff --git a/proj1/test_cases/q2/graph_bfs_vs_dfs.solution b/proj1/test_cases/q2/graph_bfs_vs_dfs.solution new file mode 100755 index 0000000..05eecc8 --- /dev/null +++ b/proj1/test_cases/q2/graph_bfs_vs_dfs.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q2/graph_bfs_vs_dfs.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->G" +expanded_states: "A B" +rev_solution: "1:A->G" +rev_expanded_states: "A D" diff --git a/proj1/test_cases/q2/graph_bfs_vs_dfs.test b/proj1/test_cases/q2/graph_bfs_vs_dfs.test new file mode 100755 index 0000000..47b78a6 --- /dev/null +++ b/proj1/test_cases/q2/graph_bfs_vs_dfs.test @@ -0,0 +1,30 @@ +# Graph where BFS finds the optimal solution but DFS does not +class: "GraphSearchTest" +algorithm: "breadthFirstSearch" + +diagram: """ +/-- B +| ^ +| | +| *A -->[G] +| | ^ +| V | +\-->D ----/ + +A is the start state, G is the goal. Arrows +mark possible transitions +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +A 1:A->G G 2.0 +A 2:A->D D 4.0 +B 0:B->D D 8.0 +D 0:D->G G 16.0 +""" diff --git a/proj1/test_cases/q2/graph_infinite.solution b/proj1/test_cases/q2/graph_infinite.solution new file mode 100755 index 0000000..17b621c --- /dev/null +++ b/proj1/test_cases/q2/graph_infinite.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q2/graph_infinite.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "0:A->B 1:B->C 1:C->G" +expanded_states: "A B C" +rev_solution: "0:A->B 1:B->C 1:C->G" +rev_expanded_states: "A B C" diff --git a/proj1/test_cases/q2/graph_infinite.test b/proj1/test_cases/q2/graph_infinite.test new file mode 100755 index 0000000..2cae9ad --- /dev/null +++ b/proj1/test_cases/q2/graph_infinite.test @@ -0,0 +1,30 @@ +# Graph where natural action choice leads to an infinite loop +class: "GraphSearchTest" +algorithm: "breadthFirstSearch" + +diagram: """ + B <--> C + ^ /| + | / | + V / V +*A<-/ [G] + +A is the start state, G is the goal. Arrows mark +possible state transitions. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +B 0:B->A A 2.0 +B 1:B->C C 4.0 +C 0:C->A A 8.0 +C 1:C->G G 16.0 +C 2:C->B B 32.0 +""" + diff --git a/proj1/test_cases/q2/graph_manypaths.solution b/proj1/test_cases/q2/graph_manypaths.solution new file mode 100755 index 0000000..0cea422 --- /dev/null +++ b/proj1/test_cases/q2/graph_manypaths.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q2/graph_manypaths.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->C 0:C->D 1:D->F 0:F->G" +expanded_states: "A B1 C B2 D E1 F E2" +rev_solution: "1:A->C 0:C->D 1:D->F 0:F->G" +rev_expanded_states: "A B2 C B1 D E2 F E1" diff --git a/proj1/test_cases/q2/graph_manypaths.test b/proj1/test_cases/q2/graph_manypaths.test new file mode 100755 index 0000000..7c636ea --- /dev/null +++ b/proj1/test_cases/q2/graph_manypaths.test @@ -0,0 +1,39 @@ +class: "GraphSearchTest" +algorithm: "breadthFirstSearch" + +diagram: """ + B1 E1 + ^ \ ^ \ + / V / V +*A --> C --> D --> F --> [G] + \ ^ \ ^ + V / V / + B2 E2 + +A is the start state, G is the goal. Arrows mark +possible state transitions. This graph has multiple +paths to the goal, where nodes with the same state +are added to the fringe multiple times before they +are expanded. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B1 B1 1.0 +A 1:A->C C 2.0 +A 2:A->B2 B2 4.0 +B1 0:B1->C C 8.0 +B2 0:B2->C C 16.0 +C 0:C->D D 32.0 +D 0:D->E1 E1 64.0 +D 1:D->F F 128.0 +D 2:D->E2 E2 256.0 +E1 0:E1->F F 512.0 +E2 0:E2->F F 1024.0 +F 0:F->G G 2048.0 +""" diff --git a/proj1/test_cases/q2/pacman_1.solution b/proj1/test_cases/q2/pacman_1.solution new file mode 100755 index 0000000..8f6d2bd --- /dev/null +++ b/proj1/test_cases/q2/pacman_1.solution @@ -0,0 +1,22 @@ +# This is the solution file for test_cases/q2/pacman_1.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +# Number of nodes expanded must be with a factor of 1.0 of the numbers below. +solution: """ +West West West West West West West West West South South East East +South South South West West West North West West West West South South +South East East East East East East East South South South South South +South South West West West West West West West West West West West +West West West West West West South West West West West West West West +West West +""" +expanded_nodes: "269" +rev_solution: """ +West West West West West West West West West South South East East +South South South West West West North West West West West South South +South East East East East East East East South South South South South +South South West West West West West West West West West West West +West West West West West West South West West West West West West West +West West +""" +rev_expanded_nodes: "269" diff --git a/proj1/test_cases/q2/pacman_1.test b/proj1/test_cases/q2/pacman_1.test new file mode 100755 index 0000000..c913f0c --- /dev/null +++ b/proj1/test_cases/q2/pacman_1.test @@ -0,0 +1,27 @@ +# This is a basic breadth first search test +class: "PacmanSearchTest" +algorithm: "breadthFirstSearch" + +# The following specifies the layout to be used +layoutName: "mediumMaze" +layout: """ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% % +% %% % % %%%%%%% %% % +% %% % % % % %%%% %%%%%%%%% %% %%%%% +% %% % % % % %% %% % +% %% % % % % % %%%% %%% %%%%%% % +% % % % % % %% %%%%%%%% % +% %% % % %%%%%%%% %% %% %%%%% +% %% % %% %%%%%%%%% %% % +% %%%%%% %%%%%%% %% %%%%%% % +%%%%%% % %%%% %% % % +% %%%%%% %%%%% % %% %% %%%%% +% %%%%%% % %%%%% %% % +% %%%%%% %%%%%%%%%%% %% %% % +%%%%%%%%%% %%%%%% % +%. %%%%%%%%%%%%%%%% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +""" + diff --git a/proj1/test_cases/q3/CONFIG b/proj1/test_cases/q3/CONFIG new file mode 100755 index 0000000..6d63286 --- /dev/null +++ b/proj1/test_cases/q3/CONFIG @@ -0,0 +1,3 @@ +class: "PassAllTestsQuestion" +max_points: "3" +timeout: "30" diff --git a/proj1/test_cases/q3/graph_backtrack.solution b/proj1/test_cases/q3/graph_backtrack.solution new file mode 100755 index 0000000..d150cb7 --- /dev/null +++ b/proj1/test_cases/q3/graph_backtrack.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q3/graph_backtrack.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->C 0:C->G" +expanded_states: "A B C D" +rev_solution: "1:A->C 0:C->G" +rev_expanded_states: "A B C D" diff --git a/proj1/test_cases/q3/graph_backtrack.test b/proj1/test_cases/q3/graph_backtrack.test new file mode 100755 index 0000000..a74bd9e --- /dev/null +++ b/proj1/test_cases/q3/graph_backtrack.test @@ -0,0 +1,32 @@ +class: "GraphSearchTest" +algorithm: "uniformCostSearch" + +diagram: """ + B + ^ + | +*A --> C --> G + | + V + D + +A is the start state, G is the goal. Arrows mark +possible state transitions. This tests whether +you extract the sequence of actions correctly even +if your search backtracks. If you fail this, your +nodes are not correctly tracking the sequences of +actions required to reach them. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +A 1:A->C C 2.0 +A 2:A->D D 4.0 +C 0:C->G G 8.0 +""" diff --git a/proj1/test_cases/q3/graph_bfs_vs_dfs.solution b/proj1/test_cases/q3/graph_bfs_vs_dfs.solution new file mode 100755 index 0000000..5dfffca --- /dev/null +++ b/proj1/test_cases/q3/graph_bfs_vs_dfs.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q3/graph_bfs_vs_dfs.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->G" +expanded_states: "A B" +rev_solution: "1:A->G" +rev_expanded_states: "A B" diff --git a/proj1/test_cases/q3/graph_bfs_vs_dfs.test b/proj1/test_cases/q3/graph_bfs_vs_dfs.test new file mode 100755 index 0000000..87aa465 --- /dev/null +++ b/proj1/test_cases/q3/graph_bfs_vs_dfs.test @@ -0,0 +1,30 @@ +# Graph where BFS finds the optimal solution but DFS does not +class: "GraphSearchTest" +algorithm: "uniformCostSearch" + +diagram: """ +/-- B +| ^ +| | +| *A -->[G] +| | ^ +| V | +\-->D ----/ + +A is the start state, G is the goal. Arrows +mark possible transitions +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +A 1:A->G G 2.0 +A 2:A->D D 4.0 +B 0:B->D D 8.0 +D 0:D->G G 16.0 +""" diff --git a/proj1/test_cases/q3/graph_infinite.solution b/proj1/test_cases/q3/graph_infinite.solution new file mode 100755 index 0000000..c6cd195 --- /dev/null +++ b/proj1/test_cases/q3/graph_infinite.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q3/graph_infinite.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "0:A->B 1:B->C 1:C->G" +expanded_states: "A B C" +rev_solution: "0:A->B 1:B->C 1:C->G" +rev_expanded_states: "A B C" diff --git a/proj1/test_cases/q3/graph_infinite.test b/proj1/test_cases/q3/graph_infinite.test new file mode 100755 index 0000000..80d807f --- /dev/null +++ b/proj1/test_cases/q3/graph_infinite.test @@ -0,0 +1,30 @@ +# Graph where natural action choice leads to an infinite loop +class: "GraphSearchTest" +algorithm: "uniformCostSearch" + +diagram: """ + B <--> C + ^ /| + | / | + V / V +*A<-/ [G] + +A is the start state, G is the goal. Arrows mark +possible state transitions. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +B 0:B->A A 2.0 +B 1:B->C C 4.0 +C 0:C->A A 8.0 +C 1:C->G G 16.0 +C 2:C->B B 32.0 +""" + diff --git a/proj1/test_cases/q3/graph_manypaths.solution b/proj1/test_cases/q3/graph_manypaths.solution new file mode 100755 index 0000000..628568f --- /dev/null +++ b/proj1/test_cases/q3/graph_manypaths.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q3/graph_manypaths.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->C 0:C->D 1:D->F 0:F->G" +expanded_states: "A B1 C B2 D E1 F E2" +rev_solution: "1:A->C 0:C->D 1:D->F 0:F->G" +rev_expanded_states: "A B1 C B2 D E1 F E2" diff --git a/proj1/test_cases/q3/graph_manypaths.test b/proj1/test_cases/q3/graph_manypaths.test new file mode 100755 index 0000000..8c39dc7 --- /dev/null +++ b/proj1/test_cases/q3/graph_manypaths.test @@ -0,0 +1,39 @@ +class: "GraphSearchTest" +algorithm: "uniformCostSearch" + +diagram: """ + B1 E1 + ^ \ ^ \ + / V / V +*A --> C --> D --> F --> [G] + \ ^ \ ^ + V / V / + B2 E2 + +A is the start state, G is the goal. Arrows mark +possible state transitions. This graph has multiple +paths to the goal, where nodes with the same state +are added to the fringe multiple times before they +are expanded. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B1 B1 1.0 +A 1:A->C C 2.0 +A 2:A->B2 B2 4.0 +B1 0:B1->C C 8.0 +B2 0:B2->C C 16.0 +C 0:C->D D 32.0 +D 0:D->E1 E1 64.0 +D 1:D->F F 128.0 +D 2:D->E2 E2 256.0 +E1 0:E1->F F 512.0 +E2 0:E2->F F 1024.0 +F 0:F->G G 2048.0 +""" diff --git a/proj1/test_cases/q3/ucs_0_graph.solution b/proj1/test_cases/q3/ucs_0_graph.solution new file mode 100755 index 0000000..b8c1509 --- /dev/null +++ b/proj1/test_cases/q3/ucs_0_graph.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q3/ucs_0_graph.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "Right Down Down" +expanded_states: "A B D C G" +rev_solution: "Right Down Down" +rev_expanded_states: "A B D C G" diff --git a/proj1/test_cases/q3/ucs_0_graph.test b/proj1/test_cases/q3/ucs_0_graph.test new file mode 100755 index 0000000..e8f3d4c --- /dev/null +++ b/proj1/test_cases/q3/ucs_0_graph.test @@ -0,0 +1,39 @@ +class: "GraphSearchTest" +algorithm: "uniformCostSearch" + +diagram: """ + C + ^ + | 2 + 2 V 4 +*A <----> B -----> [H] + |1 + 1.5 V 2.5 + G <----- D -----> E + | + 2 | + V + [F] + +A is the start state, F and H is the goal. Arrows mark possible state +transitions. The number next to the arrow is the cost of that transition. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: H F +A Right B 2.0 +B Right H 4.0 +B Down D 1.0 +B Up C 2.0 +B Left A 2.0 +C Down B 2.0 +D Right E 2.5 +D Down F 2.0 +D Left G 1.5 +""" + diff --git a/proj1/test_cases/q3/ucs_1_problemC.solution b/proj1/test_cases/q3/ucs_1_problemC.solution new file mode 100755 index 0000000..dc8fc4c --- /dev/null +++ b/proj1/test_cases/q3/ucs_1_problemC.solution @@ -0,0 +1,22 @@ +# This is the solution file for test_cases/q3/ucs_1_problemC.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +# Number of nodes expanded must be with a factor of 1.1 of the numbers below. +solution: """ +West West West West West West West West West South South East East +South South South West West West North West West West West South South +South East East East East East East East South South South South South +South South West West West West West West West West West West West +West West West West West West South West West West West West West West +West West +""" +expanded_nodes: "269" +rev_solution: """ +West West West West West West West West West South South East East +South South South West West West North West West West West South South +South East East East East East East East South South South South South +South South West West West West West West West West West West West +West West West West West West South West West West West West West West +West West +""" +rev_expanded_nodes: "269" diff --git a/proj1/test_cases/q3/ucs_1_problemC.test b/proj1/test_cases/q3/ucs_1_problemC.test new file mode 100755 index 0000000..1ce714d --- /dev/null +++ b/proj1/test_cases/q3/ucs_1_problemC.test @@ -0,0 +1,28 @@ +class: "PacmanSearchTest" +algorithm: "uniformCostSearch" +points: "0.5" + +# The following specifies the layout to be used +layoutName: "mediumMaze" +layout: """ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% % +% %% % % %%%%%%% %% % +% %% % % % % %%%% %%%%%%%%% %% %%%%% +% %% % % % % %% %% % +% %% % % % % % %%%% %%% %%%%%% % +% % % % % % %% %%%%%%%% % +% %% % % %%%%%%%% %% %% %%%%% +% %% % %% %%%%%%%%% %% % +% %%%%%% %%%%%%% %% %%%%%% % +%%%%%% % %%%% %% % % +% %%%%%% %%%%% % %% %% %%%%% +% %%%%%% % %%%%% %% % +% %%%%%% %%%%%%%%%%% %% %% % +%%%%%%%%%% %%%%%% % +%. %%%%%%%%%%%%%%%% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +""" +leewayFactor: "1.1" +#costFn: "lambda pos: 1" diff --git a/proj1/test_cases/q3/ucs_2_problemE.solution b/proj1/test_cases/q3/ucs_2_problemE.solution new file mode 100755 index 0000000..d84245f --- /dev/null +++ b/proj1/test_cases/q3/ucs_2_problemE.solution @@ -0,0 +1,22 @@ +# This is the solution file for test_cases/q3/ucs_2_problemE.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +# Number of nodes expanded must be with a factor of 1.1 of the numbers below. +solution: """ +South South West West West West South South East East East East South +South West West West West South South East East East East South South +West West West West South South East East East East South South South +West West West West West West West North West West West West West West +West West West West West West West West West West West South West West +West West West West West West West +""" +expanded_nodes: "260" +rev_solution: """ +South South West West West West South South East East East East South +South West West West West South South East East East East South South +West West West West South South East East East East South South South +West West West West West West West North West West West West West West +West West West West West West West West West West West South West West +West West West West West West West +""" +rev_expanded_nodes: "260" diff --git a/proj1/test_cases/q3/ucs_2_problemE.test b/proj1/test_cases/q3/ucs_2_problemE.test new file mode 100755 index 0000000..3c609f2 --- /dev/null +++ b/proj1/test_cases/q3/ucs_2_problemE.test @@ -0,0 +1,28 @@ +class: "PacmanSearchTest" +algorithm: "uniformCostSearch" +points: "0.5" + +# The following specifies the layout to be used +layoutName: "mediumMaze" +layout: """ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% % +% %% % % %%%%%%% %% % +% %% % % % % %%%% %%%%%%%%% %% %%%%% +% %% % % % % %% %% % +% %% % % % % % %%%% %%% %%%%%% % +% % % % % % %% %%%%%%%% % +% %% % % %%%%%%%% %% %% %%%%% +% %% % %% %%%%%%%%% %% % +% %%%%%% %%%%%%% %% %%%%%% % +%%%%%% % %%%% %% % % +% %%%%%% %%%%% % %% %% %%%%% +% %%%%%% % %%%%% %% % +% %%%%%% %%%%%%%%%%% %% %% % +%%%%%%%%%% %%%%%% % +%. %%%%%%%%%%%%%%%% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +""" +leewayFactor: "1.1" +costFn: "lambda pos: .5 ** pos[0]" diff --git a/proj1/test_cases/q3/ucs_3_problemW.solution b/proj1/test_cases/q3/ucs_3_problemW.solution new file mode 100755 index 0000000..e04325d --- /dev/null +++ b/proj1/test_cases/q3/ucs_3_problemW.solution @@ -0,0 +1,34 @@ +# This is the solution file for test_cases/q3/ucs_3_problemW.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +# Number of nodes expanded must be with a factor of 1.1 of the numbers below. +solution: """ +West West West West West West West West West West West West West West +West West West West West West West West West West West West West West +West West West West West South South South South South South South +South South East East East North North North North North North North +East East South South South South South South East East North North +North North North North East East South South South South East East +North North East East South South East East East South South West West +West West West West South South West West West West West South West +West West West West South South East East East East East East East +North East East East East East North North East East East East East +East South South West West West West South South West West West West +West South West West West West West West West West West +""" +expanded_nodes: "173" +rev_solution: """ +West West West West West West West West West West West West West West +West West West West West West West West West West West West West West +West West West West West South South South South South South South +South South East East East North North North North North North North +East East South South South South South South East East North North +North North North North East East South South South South East East +North North East East South South East East East South South West West +West West West West South South West West West West West South West +West West West West South South East East East East East East East +North East East East East East North North East East East East East +East South South West West West West South South West West West West +West South West West West West West West West West West +""" +rev_expanded_nodes: "173" diff --git a/proj1/test_cases/q3/ucs_3_problemW.test b/proj1/test_cases/q3/ucs_3_problemW.test new file mode 100755 index 0000000..fbc2fad --- /dev/null +++ b/proj1/test_cases/q3/ucs_3_problemW.test @@ -0,0 +1,28 @@ +class: "PacmanSearchTest" +algorithm: "uniformCostSearch" +points: "0.5" + +# The following specifies the layout to be used +layoutName: "mediumMaze" +layout: """ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% % +% %% % % %%%%%%% %% % +% %% % % % % %%%% %%%%%%%%% %% %%%%% +% %% % % % % %% %% % +% %% % % % % % %%%% %%% %%%%%% % +% % % % % % %% %%%%%%%% % +% %% % % %%%%%%%% %% %% %%%%% +% %% % %% %%%%%%%%% %% % +% %%%%%% %%%%%%% %% %%%%%% % +%%%%%% % %%%% %% % % +% %%%%%% %%%%% % %% %% %%%%% +% %%%%%% % %%%%% %% % +% %%%%%% %%%%%%%%%%% %% %% % +%%%%%%%%%% %%%%%% % +%. %%%%%%%%%%%%%%%% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +""" +leewayFactor: "1.1" +costFn: "lambda pos: 2 ** pos[0]" diff --git a/proj1/test_cases/q3/ucs_4_testSearch.solution b/proj1/test_cases/q3/ucs_4_testSearch.solution new file mode 100755 index 0000000..b8c5303 --- /dev/null +++ b/proj1/test_cases/q3/ucs_4_testSearch.solution @@ -0,0 +1,12 @@ +# This is the solution file for test_cases/q3/ucs_4_testSearch.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +# Number of nodes expanded must be with a factor of 2.0 of the numbers below. +solution: """ +West East East South South West West +""" +expanded_nodes: "14" +rev_solution: """ +West East East South South West West +""" +rev_expanded_nodes: "13" diff --git a/proj1/test_cases/q3/ucs_4_testSearch.test b/proj1/test_cases/q3/ucs_4_testSearch.test new file mode 100755 index 0000000..a16c6d8 --- /dev/null +++ b/proj1/test_cases/q3/ucs_4_testSearch.test @@ -0,0 +1,16 @@ +class: "PacmanSearchTest" +algorithm: "uniformCostSearch" +points: "0.5" + +# The following specifies the layout to be used +layoutName: "testSearch" +layout: """ +%%%%% +%.P % +%%% % +%. % +%%%%% +""" +searchProblemClass: "FoodSearchProblem" +leewayFactor: "2" + diff --git a/proj1/test_cases/q3/ucs_5_goalAtDequeue.solution b/proj1/test_cases/q3/ucs_5_goalAtDequeue.solution new file mode 100755 index 0000000..7d6c982 --- /dev/null +++ b/proj1/test_cases/q3/ucs_5_goalAtDequeue.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q3/ucs_5_goalAtDequeue.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->B 0:B->C 0:C->G" +expanded_states: "A B C" +rev_solution: "1:A->B 0:B->C 0:C->G" +rev_expanded_states: "A B C" diff --git a/proj1/test_cases/q3/ucs_5_goalAtDequeue.test b/proj1/test_cases/q3/ucs_5_goalAtDequeue.test new file mode 100755 index 0000000..72b35bc --- /dev/null +++ b/proj1/test_cases/q3/ucs_5_goalAtDequeue.test @@ -0,0 +1,29 @@ +class: "GraphSearchTest" +algorithm: "uniformCostSearch" + +diagram: """ + 1 1 1 +*A ---> B ---> C ---> [G] + | ^ + | 10 | + \---------------------/ + +A is the start state, G is the goal. Arrows mark possible state +transitions. The number next to the arrow is the cost of that transition. + +If you fail this test case, you may be incorrectly testing if a node is a goal +before adding it into the queue, instead of testing when you remove the node +from the queue. See the algorithm pseudocode in lecture. +""" + +graph: """ +start_state: A +goal_states: G +A 0:A->G G 10.0 +A 1:A->B B 1.0 +B 0:B->C C 1.0 +C 0:C->G G 1.0 +""" +# We only care about the solution, not the expansion order. +exactExpansionOrder: "False" + diff --git a/proj1/test_cases/q4/CONFIG b/proj1/test_cases/q4/CONFIG new file mode 100755 index 0000000..fbbb15e --- /dev/null +++ b/proj1/test_cases/q4/CONFIG @@ -0,0 +1,3 @@ +class: "PassAllTestsQuestion" +max_points: "3" +timeout: "30" \ No newline at end of file diff --git a/proj1/test_cases/q4/astar_0.solution b/proj1/test_cases/q4/astar_0.solution new file mode 100755 index 0000000..459cadd --- /dev/null +++ b/proj1/test_cases/q4/astar_0.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q4/astar_0.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "Right Down Down" +expanded_states: "A B D C G" +rev_solution: "Right Down Down" +rev_expanded_states: "A B D C G" diff --git a/proj1/test_cases/q4/astar_0.test b/proj1/test_cases/q4/astar_0.test new file mode 100755 index 0000000..9b3b539 --- /dev/null +++ b/proj1/test_cases/q4/astar_0.test @@ -0,0 +1,39 @@ +class: "GraphSearchTest" +algorithm: "aStarSearch" + +diagram: """ + C + ^ + | 2 + 2 V 4 +*A <----> B -----> [H] + | + 1.5 V 2.5 + G <----- D -----> E + | + 2 | + V + [F] + +A is the start state, F and H is the goal. Arrows mark possible state +transitions. The number next to the arrow is the cost of that transition. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: H F +A Right B 2.0 +B Right H 4.0 +B Down D 1.0 +B Up C 2.0 +B Left A 2.0 +C Down B 2.0 +D Right E 2.5 +D Down F 2.0 +D Left G 1.5 +""" + diff --git a/proj1/test_cases/q4/astar_1_graph_heuristic.solution b/proj1/test_cases/q4/astar_1_graph_heuristic.solution new file mode 100755 index 0000000..7767c27 --- /dev/null +++ b/proj1/test_cases/q4/astar_1_graph_heuristic.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q4/astar_1_graph_heuristic.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "0 0 2" +expanded_states: "S A D C" +rev_solution: "0 0 2" +rev_expanded_states: "S A D C" diff --git a/proj1/test_cases/q4/astar_1_graph_heuristic.test b/proj1/test_cases/q4/astar_1_graph_heuristic.test new file mode 100755 index 0000000..b5afd79 --- /dev/null +++ b/proj1/test_cases/q4/astar_1_graph_heuristic.test @@ -0,0 +1,54 @@ +class: "GraphSearchTest" +algorithm: "aStarSearch" + +diagram: """ + 2 3 2 + S --- A --- C ---> G + | \ / ^ +3 | \ 5 / 1 / + | \ / / + B --- D -------/ + 4 5 + +S is the start state, G is the goal. Arrows mark possible state +transitions. The number next to the arrow is the cost of that transition. + +The heuristic value of each state is: + S 6.0 + A 2.5 + B 5.25 + C 1.125 + D 1.0625 + G 0 +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: S +goal_states: G +S 0 A 2.0 +S 1 B 3.0 +S 2 D 5.0 +A 0 C 3.0 +A 1 S 2.0 +B 0 D 4.0 +B 1 S 3.0 +C 0 A 3.0 +C 1 D 1.0 +C 2 G 2.0 +D 0 B 4.0 +D 1 C 1.0 +D 2 G 5.0 +D 3 S 5.0 +""" +heuristic: """ +S 6.0 +A 2.5 +B 5.25 +C 1.125 +D 1.0625 +G 0 +""" diff --git a/proj1/test_cases/q4/astar_2_manhattan.solution b/proj1/test_cases/q4/astar_2_manhattan.solution new file mode 100755 index 0000000..65bf5f5 --- /dev/null +++ b/proj1/test_cases/q4/astar_2_manhattan.solution @@ -0,0 +1,22 @@ +# This is the solution file for test_cases/q4/astar_2_manhattan.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +# Number of nodes expanded must be with a factor of 1.1 of the numbers below. +solution: """ +West West West West West West West West West South South East East +South South South West West West North West West West West South South +South East East East East East East East South South South South South +South South West West West West West West West West West West West +West West West West West West South West West West West West West West +West West +""" +expanded_nodes: "221" +rev_solution: """ +West West West West West West West West West South South East East +South South South West West West North West West West West South South +South East East East East East East East South South South South South +South South West West West West West West West West West West West +West West West West West West South West West West West West West West +West West +""" +rev_expanded_nodes: "221" diff --git a/proj1/test_cases/q4/astar_2_manhattan.test b/proj1/test_cases/q4/astar_2_manhattan.test new file mode 100755 index 0000000..e936195 --- /dev/null +++ b/proj1/test_cases/q4/astar_2_manhattan.test @@ -0,0 +1,27 @@ +class: "PacmanSearchTest" +algorithm: "aStarSearch" + +# The following specifies the layout to be used +layoutName: "mediumMaze" +layout: """ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% P% +% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% % +% %% % % %%%%%%% %% % +% %% % % % % %%%% %%%%%%%%% %% %%%%% +% %% % % % % %% %% % +% %% % % % % % %%%% %%% %%%%%% % +% % % % % % %% %%%%%%%% % +% %% % % %%%%%%%% %% %% %%%%% +% %% % %% %%%%%%%%% %% % +% %%%%%% %%%%%%% %% %%%%%% % +%%%%%% % %%%% %% % % +% %%%%%% %%%%% % %% %% %%%%% +% %%%%%% % %%%%% %% % +% %%%%%% %%%%%%%%%%% %% %% % +%%%%%%%%%% %%%%%% % +%. %%%%%%%%%%%%%%%% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +""" +leewayFactor: "1.1" +heuristic: "manhattanHeuristic" diff --git a/proj1/test_cases/q4/astar_3_goalAtDequeue.solution b/proj1/test_cases/q4/astar_3_goalAtDequeue.solution new file mode 100755 index 0000000..edb3502 --- /dev/null +++ b/proj1/test_cases/q4/astar_3_goalAtDequeue.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q4/astar_3_goalAtDequeue.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->B 0:B->C 0:C->G" +expanded_states: "A B C" +rev_solution: "1:A->B 0:B->C 0:C->G" +rev_expanded_states: "A B C" diff --git a/proj1/test_cases/q4/astar_3_goalAtDequeue.test b/proj1/test_cases/q4/astar_3_goalAtDequeue.test new file mode 100755 index 0000000..c4d1903 --- /dev/null +++ b/proj1/test_cases/q4/astar_3_goalAtDequeue.test @@ -0,0 +1,29 @@ +class: "GraphSearchTest" +algorithm: "aStarSearch" + +diagram: """ + 1 1 1 +*A ---> B ---> C ---> [G] + | ^ + | 10 | + \---------------------/ + +A is the start state, G is the goal. Arrows mark possible state +transitions. The number next to the arrow is the cost of that transition. + +If you fail this test case, you may be incorrectly testing if a node is a goal +before adding it into the queue, instead of testing when you remove the node +from the queue. See the algorithm pseudocode in lecture. +""" + +graph: """ +start_state: A +goal_states: G +A 0:A->G G 10.0 +A 1:A->B B 1.0 +B 0:B->C C 1.0 +C 0:C->G G 1.0 +""" +# We only care about the solution, not the expansion order. +exactExpansionOrder: "False" + diff --git a/proj1/test_cases/q4/graph_backtrack.solution b/proj1/test_cases/q4/graph_backtrack.solution new file mode 100755 index 0000000..fc51794 --- /dev/null +++ b/proj1/test_cases/q4/graph_backtrack.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q4/graph_backtrack.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->C 0:C->G" +expanded_states: "A B C D" +rev_solution: "1:A->C 0:C->G" +rev_expanded_states: "A B C D" diff --git a/proj1/test_cases/q4/graph_backtrack.test b/proj1/test_cases/q4/graph_backtrack.test new file mode 100755 index 0000000..84e0126 --- /dev/null +++ b/proj1/test_cases/q4/graph_backtrack.test @@ -0,0 +1,32 @@ +class: "GraphSearchTest" +algorithm: "aStarSearch" + +diagram: """ + B + ^ + | +*A --> C --> G + | + V + D + +A is the start state, G is the goal. Arrows mark +possible state transitions. This tests whether +you extract the sequence of actions correctly even +if your search backtracks. If you fail this, your +nodes are not correctly tracking the sequences of +actions required to reach them. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B B 1.0 +A 1:A->C C 2.0 +A 2:A->D D 4.0 +C 0:C->G G 8.0 +""" diff --git a/proj1/test_cases/q4/graph_manypaths.solution b/proj1/test_cases/q4/graph_manypaths.solution new file mode 100755 index 0000000..0caa767 --- /dev/null +++ b/proj1/test_cases/q4/graph_manypaths.solution @@ -0,0 +1,7 @@ +# This is the solution file for test_cases/q4/graph_manypaths.test. +# This solution is designed to support both right-to-left +# and left-to-right implementations. +solution: "1:A->C 0:C->D 1:D->F 0:F->G" +expanded_states: "A B1 C B2 D E1 F E2" +rev_solution: "1:A->C 0:C->D 1:D->F 0:F->G" +rev_expanded_states: "A B1 C B2 D E1 F E2" diff --git a/proj1/test_cases/q4/graph_manypaths.test b/proj1/test_cases/q4/graph_manypaths.test new file mode 100755 index 0000000..82fdf87 --- /dev/null +++ b/proj1/test_cases/q4/graph_manypaths.test @@ -0,0 +1,39 @@ +class: "GraphSearchTest" +algorithm: "aStarSearch" + +diagram: """ + B1 E1 + ^ \ ^ \ + / V / V +*A --> C --> D --> F --> [G] + \ ^ \ ^ + V / V / + B2 E2 + +A is the start state, G is the goal. Arrows mark +possible state transitions. This graph has multiple +paths to the goal, where nodes with the same state +are added to the fringe multiple times before they +are expanded. +""" +# The following section specifies the search problem and the solution. +# The graph is specified by first the set of start states, followed by +# the set of goal states, and lastly by the state transitions which are +# of the form: +# +graph: """ +start_state: A +goal_states: G +A 0:A->B1 B1 1.0 +A 1:A->C C 2.0 +A 2:A->B2 B2 4.0 +B1 0:B1->C C 8.0 +B2 0:B2->C C 16.0 +C 0:C->D D 32.0 +D 0:D->E1 E1 64.0 +D 1:D->F F 128.0 +D 2:D->E2 E2 256.0 +E1 0:E1->F F 512.0 +E2 0:E2->F F 1024.0 +F 0:F->G G 2048.0 +""" diff --git a/proj1/test_cases/q5/CONFIG b/proj1/test_cases/q5/CONFIG new file mode 100755 index 0000000..94e47f6 --- /dev/null +++ b/proj1/test_cases/q5/CONFIG @@ -0,0 +1,4 @@ +class: "PassAllTestsQuestion" +max_points: "3" +depends: "q2" +timeout: "30" \ No newline at end of file diff --git a/proj1/test_cases/q5/corner_tiny_corner.solution b/proj1/test_cases/q5/corner_tiny_corner.solution new file mode 100755 index 0000000..161bf15 --- /dev/null +++ b/proj1/test_cases/q5/corner_tiny_corner.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q5/corner_tiny_corner.test. +solution_length: "28" diff --git a/proj1/test_cases/q5/corner_tiny_corner.test b/proj1/test_cases/q5/corner_tiny_corner.test new file mode 100755 index 0000000..823bd47 --- /dev/null +++ b/proj1/test_cases/q5/corner_tiny_corner.test @@ -0,0 +1,14 @@ +class: "CornerProblemTest" + +layoutName: "tinyCorner" +layout: """ +%%%%%%%% +%. .% +% P % +% %%%% % +% % % +% % %%%% +%.% .% +%%%%%%%% +""" + diff --git a/proj1/test_cases/q6/CONFIG b/proj1/test_cases/q6/CONFIG new file mode 100755 index 0000000..1641312 --- /dev/null +++ b/proj1/test_cases/q6/CONFIG @@ -0,0 +1,4 @@ +class: "Q6PartialCreditQuestion" +max_points: "3" +depends: "q4" +timeout: "30" \ No newline at end of file diff --git a/proj1/test_cases/q6/corner_sanity_1.solution b/proj1/test_cases/q6/corner_sanity_1.solution new file mode 100755 index 0000000..4385d9b --- /dev/null +++ b/proj1/test_cases/q6/corner_sanity_1.solution @@ -0,0 +1,7 @@ +# In order for a heuristic to be admissible, the value +# of the heuristic must be less at each state than the +# true cost of the optimal path from that state to a goal. +cost: "8" +path: """ +North South South East East East North North +""" diff --git a/proj1/test_cases/q6/corner_sanity_1.test b/proj1/test_cases/q6/corner_sanity_1.test new file mode 100755 index 0000000..93379ac --- /dev/null +++ b/proj1/test_cases/q6/corner_sanity_1.test @@ -0,0 +1,12 @@ +class: "CornerHeuristicSanity" +points: "1" + +# The following specifies the layout to be used +layout: """ +%%%%%% +%. .% +%P % +%. .% +%%%%%% +""" + diff --git a/proj1/test_cases/q6/corner_sanity_2.solution b/proj1/test_cases/q6/corner_sanity_2.solution new file mode 100755 index 0000000..1aebe8a --- /dev/null +++ b/proj1/test_cases/q6/corner_sanity_2.solution @@ -0,0 +1,7 @@ +# In order for a heuristic to be admissible, the value +# of the heuristic must be less at each state than the +# true cost of the optimal path from that state to a goal. +cost: "8" +path: """ +West North North East East East South South +""" diff --git a/proj1/test_cases/q6/corner_sanity_2.test b/proj1/test_cases/q6/corner_sanity_2.test new file mode 100755 index 0000000..18184a8 --- /dev/null +++ b/proj1/test_cases/q6/corner_sanity_2.test @@ -0,0 +1,12 @@ +class: "CornerHeuristicSanity" +points: "1" + +# The following specifies the layout to be used +layout: """ +%%%%%% +%. .% +% %% % +%.P%.% +%%%%%% +""" + diff --git a/proj1/test_cases/q6/corner_sanity_3.solution b/proj1/test_cases/q6/corner_sanity_3.solution new file mode 100755 index 0000000..c02dd57 --- /dev/null +++ b/proj1/test_cases/q6/corner_sanity_3.solution @@ -0,0 +1,9 @@ +# In order for a heuristic to be admissible, the value +# of the heuristic must be less at each state than the +# true cost of the optimal path from that state to a goal. +cost: "28" +path: """ +South South South West West West West East East East East East North +North North North North West West West South South South West West +North North North +""" diff --git a/proj1/test_cases/q6/corner_sanity_3.test b/proj1/test_cases/q6/corner_sanity_3.test new file mode 100755 index 0000000..8f30442 --- /dev/null +++ b/proj1/test_cases/q6/corner_sanity_3.test @@ -0,0 +1,15 @@ +class: "CornerHeuristicSanity" +points: "1" + +# The following specifies the layout to be used +layout: """ +%%%%%%%% +%.% .% +% % % % +% % %P % +% % % +%%%%% % +%. .% +%%%%%%%% +""" + diff --git a/proj1/test_cases/q6/medium_corners.solution b/proj1/test_cases/q6/medium_corners.solution new file mode 100755 index 0000000..913dc45 --- /dev/null +++ b/proj1/test_cases/q6/medium_corners.solution @@ -0,0 +1,16 @@ +# This solution file specifies the length of the optimal path +# as well as the thresholds on number of nodes expanded to be +# used in scoring. +cost: "106" +path: """ +North East East East East North North West West West West North North +North North North North North North West West West West South South +East East East East South South South South South South West West +South South South West West East East North North North East East East +East East East East East South South East East East East East North +North East East North North East East North North East East East East +South South South South East East North North East East South South +South South South North North North North North North North West West +North North East East North North +""" +thresholds: "2000 1600 1200" diff --git a/proj1/test_cases/q6/medium_corners.test b/proj1/test_cases/q6/medium_corners.test new file mode 100755 index 0000000..dfa0a68 --- /dev/null +++ b/proj1/test_cases/q6/medium_corners.test @@ -0,0 +1,19 @@ +class: "CornerHeuristicPacman" + +# The following specifies the layout to be used +layout: """ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%. % % % %.% +% % % %%%%%% %%%%%%% % % +% % % % % % +%%%%% %%%%% %%% %% %%%%% % %%% +% % % % % % % % % +% %%% % % % %%%%%%%% %%% %%% % +% % %% % % % % +%%% % %%%%%%% %%%% %%% % % % % +% % %% % % % +% % %%%%% % %%%% % %%% %%% % % +% % % % % % %%% % +%. %P%%%%% % %%% % .% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +""" diff --git a/proj1/test_cases/q7/CONFIG b/proj1/test_cases/q7/CONFIG new file mode 100755 index 0000000..e315c65 --- /dev/null +++ b/proj1/test_cases/q7/CONFIG @@ -0,0 +1,4 @@ +class: "PartialCreditQuestion" +max_points: "4" +depends: "q4" +timeout: "40" \ No newline at end of file diff --git a/proj1/test_cases/q7/food_heuristic_1.solution b/proj1/test_cases/q7/food_heuristic_1.solution new file mode 100755 index 0000000..7a287f8 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_1.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_1.test. +solution_cost: "0" diff --git a/proj1/test_cases/q7/food_heuristic_1.test b/proj1/test_cases/q7/food_heuristic_1.test new file mode 100755 index 0000000..7545a7a --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_1.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 1" +layout: """ +%%%%%% +% % +% % +%P % +%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_10.solution b/proj1/test_cases/q7/food_heuristic_10.solution new file mode 100755 index 0000000..1917f05 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_10.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_10.test. +solution_cost: "7" diff --git a/proj1/test_cases/q7/food_heuristic_10.test b/proj1/test_cases/q7/food_heuristic_10.test new file mode 100755 index 0000000..212c7bd --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_10.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 10" +layout: """ +%%%%%%%% +% % +%. P .% +% % +%%%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_11.solution b/proj1/test_cases/q7/food_heuristic_11.solution new file mode 100755 index 0000000..11c3289 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_11.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_11.test. +solution_cost: "8" diff --git a/proj1/test_cases/q7/food_heuristic_11.test b/proj1/test_cases/q7/food_heuristic_11.test new file mode 100755 index 0000000..f5e6ed4 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_11.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 11" +layout: """ +%%%%%%%% +% % +% P % +%. . .% +%%%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_12.solution b/proj1/test_cases/q7/food_heuristic_12.solution new file mode 100755 index 0000000..0edcc02 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_12.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_12.test. +solution_cost: "1" diff --git a/proj1/test_cases/q7/food_heuristic_12.test b/proj1/test_cases/q7/food_heuristic_12.test new file mode 100755 index 0000000..cc99a25 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_12.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 12" +layout: """ +%%%%%%%% +% % +% P.% +% % +%%%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_13.solution b/proj1/test_cases/q7/food_heuristic_13.solution new file mode 100755 index 0000000..c25d50b --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_13.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_13.test. +solution_cost: "5" diff --git a/proj1/test_cases/q7/food_heuristic_13.test b/proj1/test_cases/q7/food_heuristic_13.test new file mode 100755 index 0000000..09d6f1e --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_13.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 13" +layout: """ +%%%%%%%% +% % +%P. .% +% % +%%%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_14.solution b/proj1/test_cases/q7/food_heuristic_14.solution new file mode 100755 index 0000000..e6cc475 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_14.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_14.test. +solution_cost: "31" diff --git a/proj1/test_cases/q7/food_heuristic_14.test b/proj1/test_cases/q7/food_heuristic_14.test new file mode 100755 index 0000000..58982e3 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_14.test @@ -0,0 +1,19 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 14" +layout: """ +%%%%%%%%%% +% % +% ...%...% +% .%.%.%.% +% .%.%.%.% +% .%.%.%.% +% .%.%.%.% +% .%.%.%.% +%P.%...%.% +% % +%%%%%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_15.solution b/proj1/test_cases/q7/food_heuristic_15.solution new file mode 100755 index 0000000..4eca0f1 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_15.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_15.test. +solution_cost: "21" diff --git a/proj1/test_cases/q7/food_heuristic_15.test b/proj1/test_cases/q7/food_heuristic_15.test new file mode 100755 index 0000000..df605c1 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_15.test @@ -0,0 +1,32 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 15" +layout: """ +%%% +% % +% % +% % +% % +% % +%.% +%.% +% % +% % +% % +% % +% % +% % +% % +%.% +% % +%P% +% % +% % +% % +% % +%.% +%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_16.solution b/proj1/test_cases/q7/food_heuristic_16.solution new file mode 100755 index 0000000..8d89992 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_16.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_16.test. +solution_cost: "7" diff --git a/proj1/test_cases/q7/food_heuristic_16.test b/proj1/test_cases/q7/food_heuristic_16.test new file mode 100755 index 0000000..762b433 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_16.test @@ -0,0 +1,15 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 16" +layout: """ +%%%% +% .% +% % +%P % +% % +% .% +%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_17.solution b/proj1/test_cases/q7/food_heuristic_17.solution new file mode 100755 index 0000000..63a9a1b --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_17.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_17.test. +solution_cost: "16" diff --git a/proj1/test_cases/q7/food_heuristic_17.test b/proj1/test_cases/q7/food_heuristic_17.test new file mode 100755 index 0000000..a923f67 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_17.test @@ -0,0 +1,14 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 17" +layout: """ +%%%%%%%% +%.%....% +%.% %%.% +%.%P%%.% +%... .% +%%%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_2.solution b/proj1/test_cases/q7/food_heuristic_2.solution new file mode 100755 index 0000000..ca5aba1 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_2.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_2.test. +solution_cost: "0" diff --git a/proj1/test_cases/q7/food_heuristic_2.test b/proj1/test_cases/q7/food_heuristic_2.test new file mode 100755 index 0000000..956e75d --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_2.test @@ -0,0 +1,32 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 2" +layout: """ +%%% +% % +% % +% % +% % +% % +% % +% % +% % +% % +% % +% % +% % +% % +% % +% % +% % +%P% +% % +% % +% % +% % +% % +%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_3.solution b/proj1/test_cases/q7/food_heuristic_3.solution new file mode 100755 index 0000000..d1694b5 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_3.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_3.test. +solution_cost: "0" diff --git a/proj1/test_cases/q7/food_heuristic_3.test b/proj1/test_cases/q7/food_heuristic_3.test new file mode 100755 index 0000000..250a8b1 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_3.test @@ -0,0 +1,15 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 3" +layout: """ +%%%% +% % +% % +%P % +% % +% % +%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_4.solution b/proj1/test_cases/q7/food_heuristic_4.solution new file mode 100755 index 0000000..6e1e82a --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_4.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_4.test. +solution_cost: "0" diff --git a/proj1/test_cases/q7/food_heuristic_4.test b/proj1/test_cases/q7/food_heuristic_4.test new file mode 100755 index 0000000..ed86a0c --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_4.test @@ -0,0 +1,14 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 4" +layout: """ +%%%%%%%% +% % % +% % %% % +% %P%% % +% % +%%%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_5.solution b/proj1/test_cases/q7/food_heuristic_5.solution new file mode 100755 index 0000000..779e9e6 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_5.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_5.test. +solution_cost: "11" diff --git a/proj1/test_cases/q7/food_heuristic_5.test b/proj1/test_cases/q7/food_heuristic_5.test new file mode 100755 index 0000000..1f44c48 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_5.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 5" +layout: """ +%%%%%% +%....% +%....% +%P...% +%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_6.solution b/proj1/test_cases/q7/food_heuristic_6.solution new file mode 100755 index 0000000..906b510 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_6.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_6.test. +solution_cost: "5" diff --git a/proj1/test_cases/q7/food_heuristic_6.test b/proj1/test_cases/q7/food_heuristic_6.test new file mode 100755 index 0000000..01d7f32 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_6.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 6" +layout: """ +%%%%%% +% .% +%.P..% +% % +%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_7.solution b/proj1/test_cases/q7/food_heuristic_7.solution new file mode 100755 index 0000000..5994a7b --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_7.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_7.test. +solution_cost: "7" diff --git a/proj1/test_cases/q7/food_heuristic_7.test b/proj1/test_cases/q7/food_heuristic_7.test new file mode 100755 index 0000000..b1db372 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_7.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 7" +layout: """ +%%%%%%% +% .% +%. P..% +% % +%%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_8.solution b/proj1/test_cases/q7/food_heuristic_8.solution new file mode 100755 index 0000000..0e4fb08 --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_8.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_8.test. +solution_cost: "5" diff --git a/proj1/test_cases/q7/food_heuristic_8.test b/proj1/test_cases/q7/food_heuristic_8.test new file mode 100755 index 0000000..b9430af --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_8.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 8" +layout: """ +%%%%%% +% .% +% .% +%P .% +%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_9.solution b/proj1/test_cases/q7/food_heuristic_9.solution new file mode 100755 index 0000000..1470d9a --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_9.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_9.test. +solution_cost: "6" diff --git a/proj1/test_cases/q7/food_heuristic_9.test b/proj1/test_cases/q7/food_heuristic_9.test new file mode 100755 index 0000000..799b41d --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_9.test @@ -0,0 +1,13 @@ +class: "HeuristicTest" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "Test 9" +layout: """ +%%%%%% +% %. % +% %%.% +%P. .% +%%%%%% +""" + diff --git a/proj1/test_cases/q7/food_heuristic_grade_tricky.solution b/proj1/test_cases/q7/food_heuristic_grade_tricky.solution new file mode 100755 index 0000000..cd6fd7d --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_grade_tricky.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q7/food_heuristic_grade_tricky.test. +# File intentionally blank. diff --git a/proj1/test_cases/q7/food_heuristic_grade_tricky.test b/proj1/test_cases/q7/food_heuristic_grade_tricky.test new file mode 100755 index 0000000..081fb0d --- /dev/null +++ b/proj1/test_cases/q7/food_heuristic_grade_tricky.test @@ -0,0 +1,19 @@ +class: "HeuristicGrade" + +heuristic: "foodHeuristic" +searchProblemClass: "FoodSearchProblem" +layoutName: "trickySearch" +layout: """ +%%%%%%%%%%%%%%%%%%%% +%. ..% % +%.%%.%%.%%.%%.%% % % +% P % % +%%%%%%%%%%%%%%%%%% % +%..... % +%%%%%%%%%%%%%%%%%%%% +""" +# One point always, an extra point for each +# threshold passed. +basePoints: "1" +gradingThresholds: "15000 12000 9000 7000" + diff --git a/proj1/test_cases/q8/CONFIG b/proj1/test_cases/q8/CONFIG new file mode 100755 index 0000000..1727f2d --- /dev/null +++ b/proj1/test_cases/q8/CONFIG @@ -0,0 +1,3 @@ +class: "PassAllTestsQuestion" +max_points: "3" +timeout: "120" \ No newline at end of file diff --git a/proj1/test_cases/q8/closest_dot_1.solution b/proj1/test_cases/q8/closest_dot_1.solution new file mode 100755 index 0000000..300fc25 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_1.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_1.test. +solution_length: "1" diff --git a/proj1/test_cases/q8/closest_dot_1.test b/proj1/test_cases/q8/closest_dot_1.test new file mode 100755 index 0000000..672989f --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_1.test @@ -0,0 +1,11 @@ +class: "ClosestDotTest" + +layoutName: "Test 1" +layout: """ +%%%%%% +%....% +%....% +%P...% +%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_10.solution b/proj1/test_cases/q8/closest_dot_10.solution new file mode 100755 index 0000000..174b5dd --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_10.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_10.test. +solution_length: "1" diff --git a/proj1/test_cases/q8/closest_dot_10.test b/proj1/test_cases/q8/closest_dot_10.test new file mode 100755 index 0000000..b1e0f33 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_10.test @@ -0,0 +1,17 @@ +class: "ClosestDotTest" + +layoutName: "Test 10" +layout: """ +%%%%%%%%%% +% % +% ...%...% +% .%.%.%.% +% .%.%.%.% +% .%.%.%.% +% .%.%.%.% +% .%.%.%.% +%P.%...%.% +% % +%%%%%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_11.solution b/proj1/test_cases/q8/closest_dot_11.solution new file mode 100755 index 0000000..80bbe38 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_11.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_11.test. +solution_length: "2" diff --git a/proj1/test_cases/q8/closest_dot_11.test b/proj1/test_cases/q8/closest_dot_11.test new file mode 100755 index 0000000..0310a1e --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_11.test @@ -0,0 +1,30 @@ +class: "ClosestDotTest" + +layoutName: "Test 11" +layout: """ +%%% +% % +% % +% % +% % +% % +%.% +%.% +% % +% % +% % +% % +% % +% % +% % +%.% +% % +%P% +% % +% % +% % +% % +%.% +%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_12.solution b/proj1/test_cases/q8/closest_dot_12.solution new file mode 100755 index 0000000..6f38bcb --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_12.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_12.test. +solution_length: "3" diff --git a/proj1/test_cases/q8/closest_dot_12.test b/proj1/test_cases/q8/closest_dot_12.test new file mode 100755 index 0000000..a17b628 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_12.test @@ -0,0 +1,13 @@ +class: "ClosestDotTest" + +layoutName: "Test 12" +layout: """ +%%%% +% .% +% % +%P % +% % +% .% +%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_13.solution b/proj1/test_cases/q8/closest_dot_13.solution new file mode 100755 index 0000000..7afa908 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_13.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_13.test. +solution_length: "1" diff --git a/proj1/test_cases/q8/closest_dot_13.test b/proj1/test_cases/q8/closest_dot_13.test new file mode 100755 index 0000000..87c423d --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_13.test @@ -0,0 +1,12 @@ +class: "ClosestDotTest" + +layoutName: "Test 13" +layout: """ +%%%%%%%% +%.%....% +%.% %%.% +%.%P%%.% +%... .% +%%%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_2.solution b/proj1/test_cases/q8/closest_dot_2.solution new file mode 100755 index 0000000..16d75de --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_2.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_2.test. +solution_length: "1" diff --git a/proj1/test_cases/q8/closest_dot_2.test b/proj1/test_cases/q8/closest_dot_2.test new file mode 100755 index 0000000..4b59602 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_2.test @@ -0,0 +1,11 @@ +class: "ClosestDotTest" + +layoutName: "Test 2" +layout: """ +%%%%%% +% .% +%.P..% +% % +%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_3.solution b/proj1/test_cases/q8/closest_dot_3.solution new file mode 100755 index 0000000..cbd5974 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_3.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_3.test. +solution_length: "1" diff --git a/proj1/test_cases/q8/closest_dot_3.test b/proj1/test_cases/q8/closest_dot_3.test new file mode 100755 index 0000000..aa2a3af --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_3.test @@ -0,0 +1,11 @@ +class: "ClosestDotTest" + +layoutName: "Test 3" +layout: """ +%%%%%%% +% .% +%. P..% +% % +%%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_4.solution b/proj1/test_cases/q8/closest_dot_4.solution new file mode 100755 index 0000000..ca520b5 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_4.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_4.test. +solution_length: "3" diff --git a/proj1/test_cases/q8/closest_dot_4.test b/proj1/test_cases/q8/closest_dot_4.test new file mode 100755 index 0000000..8499f6d --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_4.test @@ -0,0 +1,11 @@ +class: "ClosestDotTest" + +layoutName: "Test 4" +layout: """ +%%%%%% +% .% +% .% +%P .% +%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_5.solution b/proj1/test_cases/q8/closest_dot_5.solution new file mode 100755 index 0000000..5c526a2 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_5.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_5.test. +solution_length: "1" diff --git a/proj1/test_cases/q8/closest_dot_5.test b/proj1/test_cases/q8/closest_dot_5.test new file mode 100755 index 0000000..dfaee3d --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_5.test @@ -0,0 +1,11 @@ +class: "ClosestDotTest" + +layoutName: "Test 5" +layout: """ +%%%%%% +% %. % +% %%.% +%P. .% +%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_6.solution b/proj1/test_cases/q8/closest_dot_6.solution new file mode 100755 index 0000000..b06468a --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_6.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_6.test. +solution_length: "2" diff --git a/proj1/test_cases/q8/closest_dot_6.test b/proj1/test_cases/q8/closest_dot_6.test new file mode 100755 index 0000000..bc50c57 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_6.test @@ -0,0 +1,11 @@ +class: "ClosestDotTest" + +layoutName: "Test 6" +layout: """ +%%%%%%%% +% % +%. P .% +% % +%%%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_7.solution b/proj1/test_cases/q8/closest_dot_7.solution new file mode 100755 index 0000000..3231b28 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_7.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_7.test. +solution_length: "1" diff --git a/proj1/test_cases/q8/closest_dot_7.test b/proj1/test_cases/q8/closest_dot_7.test new file mode 100755 index 0000000..746e89a --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_7.test @@ -0,0 +1,11 @@ +class: "ClosestDotTest" + +layoutName: "Test 7" +layout: """ +%%%%%%%% +% % +% P % +%. . .% +%%%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_8.solution b/proj1/test_cases/q8/closest_dot_8.solution new file mode 100755 index 0000000..646e621 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_8.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_8.test. +solution_length: "1" diff --git a/proj1/test_cases/q8/closest_dot_8.test b/proj1/test_cases/q8/closest_dot_8.test new file mode 100755 index 0000000..c266ae1 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_8.test @@ -0,0 +1,11 @@ +class: "ClosestDotTest" + +layoutName: "Test 8" +layout: """ +%%%%%%%% +% % +% P.% +% % +%%%%%%%% +""" + diff --git a/proj1/test_cases/q8/closest_dot_9.solution b/proj1/test_cases/q8/closest_dot_9.solution new file mode 100755 index 0000000..6c94aa5 --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_9.solution @@ -0,0 +1,2 @@ +# This is the solution file for test_cases/q8/closest_dot_9.test. +solution_length: "1" diff --git a/proj1/test_cases/q8/closest_dot_9.test b/proj1/test_cases/q8/closest_dot_9.test new file mode 100755 index 0000000..da078de --- /dev/null +++ b/proj1/test_cases/q8/closest_dot_9.test @@ -0,0 +1,11 @@ +class: "ClosestDotTest" + +layoutName: "Test 9" +layout: """ +%%%%%%%% +% % +%P. .% +% % +%%%%%%%% +""" + diff --git a/proj1/textDisplay.py b/proj1/textDisplay.py new file mode 100755 index 0000000..8e25a46 --- /dev/null +++ b/proj1/textDisplay.py @@ -0,0 +1,81 @@ +# textDisplay.py +# -------------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +import time +try: + import pacman +except: + pass + +DRAW_EVERY = 1 +SLEEP_TIME = 0 # This can be overwritten by __init__ +DISPLAY_MOVES = False +QUIET = False # Supresses output + +class NullGraphics: + def initialize(self, state, isBlue = False): + pass + + def update(self, state): + pass + + def checkNullDisplay(self): + return True + + def pause(self): + time.sleep(SLEEP_TIME) + + def draw(self, state): + print(state) + + def updateDistributions(self, dist): + pass + + def finish(self): + pass + +class PacmanGraphics: + def __init__(self, speed=None): + if speed != None: + global SLEEP_TIME + SLEEP_TIME = speed + + def initialize(self, state, isBlue = False): + self.draw(state) + self.pause() + self.turn = 0 + self.agentCounter = 0 + + def update(self, state): + numAgents = len(state.agentStates) + self.agentCounter = (self.agentCounter + 1) % numAgents + if self.agentCounter == 0: + self.turn += 1 + if DISPLAY_MOVES: + ghosts = [pacman.nearestPoint(state.getGhostPosition(i)) for i in range(1, numAgents)] + print("%4d) P: %-8s" % (self.turn, str(pacman.nearestPoint(state.getPacmanPosition()))),'| Score: %-5d' % state.score,'| Ghosts:', ghosts) + if self.turn % DRAW_EVERY == 0: + self.draw(state) + self.pause() + if state._win or state._lose: + self.draw(state) + + def pause(self): + time.sleep(SLEEP_TIME) + + def draw(self, state): + print(state) + + def finish(self): + pass diff --git a/proj1/util.py b/proj1/util.py new file mode 100755 index 0000000..d81667f --- /dev/null +++ b/proj1/util.py @@ -0,0 +1,672 @@ +# util.py +# ------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +# util.py +# ------- +# Licensing Information: You are free to use or extend these projects for +# educational purposes provided that (1) you do not distribute or publish +# solutions, (2) you retain this notice, and (3) you provide clear +# attribution to UC Berkeley, including a link to http://ai.berkeley.edu. +# +# Attribution Information: The Pacman AI projects were developed at UC Berkeley. +# The core projects and autograders were primarily created by John DeNero +# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). +# Student side autograding was added by Brad Miller, Nick Hay, and +# Pieter Abbeel (pabbeel@cs.berkeley.edu). + + +import sys +import inspect +import heapq, random + + +class FixedRandom: + def __init__(self): + fixedState = (3, (2147483648, 507801126, 683453281, 310439348, 2597246090, \ + 2209084787, 2267831527, 979920060, 3098657677, 37650879, 807947081, 3974896263, \ + 881243242, 3100634921, 1334775171, 3965168385, 746264660, 4074750168, 500078808, \ + 776561771, 702988163, 1636311725, 2559226045, 157578202, 2498342920, 2794591496, \ + 4130598723, 496985844, 2944563015, 3731321600, 3514814613, 3362575829, 3038768745, \ + 2206497038, 1108748846, 1317460727, 3134077628, 988312410, 1674063516, 746456451, \ + 3958482413, 1857117812, 708750586, 1583423339, 3466495450, 1536929345, 1137240525, \ + 3875025632, 2466137587, 1235845595, 4214575620, 3792516855, 657994358, 1241843248, \ + 1695651859, 3678946666, 1929922113, 2351044952, 2317810202, 2039319015, 460787996, \ + 3654096216, 4068721415, 1814163703, 2904112444, 1386111013, 574629867, 2654529343, \ + 3833135042, 2725328455, 552431551, 4006991378, 1331562057, 3710134542, 303171486, \ + 1203231078, 2670768975, 54570816, 2679609001, 578983064, 1271454725, 3230871056, \ + 2496832891, 2944938195, 1608828728, 367886575, 2544708204, 103775539, 1912402393, \ + 1098482180, 2738577070, 3091646463, 1505274463, 2079416566, 659100352, 839995305, \ + 1696257633, 274389836, 3973303017, 671127655, 1061109122, 517486945, 1379749962, \ + 3421383928, 3116950429, 2165882425, 2346928266, 2892678711, 2936066049, 1316407868, \ + 2873411858, 4279682888, 2744351923, 3290373816, 1014377279, 955200944, 4220990860, \ + 2386098930, 1772997650, 3757346974, 1621616438, 2877097197, 442116595, 2010480266, \ + 2867861469, 2955352695, 605335967, 2222936009, 2067554933, 4129906358, 1519608541, \ + 1195006590, 1942991038, 2736562236, 279162408, 1415982909, 4099901426, 1732201505, \ + 2934657937, 860563237, 2479235483, 3081651097, 2244720867, 3112631622, 1636991639, \ + 3860393305, 2312061927, 48780114, 1149090394, 2643246550, 1764050647, 3836789087, \ + 3474859076, 4237194338, 1735191073, 2150369208, 92164394, 756974036, 2314453957, \ + 323969533, 4267621035, 283649842, 810004843, 727855536, 1757827251, 3334960421, \ + 3261035106, 38417393, 2660980472, 1256633965, 2184045390, 811213141, 2857482069, \ + 2237770878, 3891003138, 2787806886, 2435192790, 2249324662, 3507764896, 995388363, \ + 856944153, 619213904, 3233967826, 3703465555, 3286531781, 3863193356, 2992340714, \ + 413696855, 3865185632, 1704163171, 3043634452, 2225424707, 2199018022, 3506117517, \ + 3311559776, 3374443561, 1207829628, 668793165, 1822020716, 2082656160, 1160606415, \ + 3034757648, 741703672, 3094328738, 459332691, 2702383376, 1610239915, 4162939394, \ + 557861574, 3805706338, 3832520705, 1248934879, 3250424034, 892335058, 74323433, \ + 3209751608, 3213220797, 3444035873, 3743886725, 1783837251, 610968664, 580745246, \ + 4041979504, 201684874, 2673219253, 1377283008, 3497299167, 2344209394, 2304982920, \ + 3081403782, 2599256854, 3184475235, 3373055826, 695186388, 2423332338, 222864327, \ + 1258227992, 3627871647, 3487724980, 4027953808, 3053320360, 533627073, 3026232514, \ + 2340271949, 867277230, 868513116, 2158535651, 2487822909, 3428235761, 3067196046, \ + 3435119657, 1908441839, 788668797, 3367703138, 3317763187, 908264443, 2252100381, \ + 764223334, 4127108988, 384641349, 3377374722, 1263833251, 1958694944, 3847832657, \ + 1253909612, 1096494446, 555725445, 2277045895, 3340096504, 1383318686, 4234428127, \ + 1072582179, 94169494, 1064509968, 2681151917, 2681864920, 734708852, 1338914021, \ + 1270409500, 1789469116, 4191988204, 1716329784, 2213764829, 3712538840, 919910444, \ + 1318414447, 3383806712, 3054941722, 3378649942, 1205735655, 1268136494, 2214009444, \ + 2532395133, 3232230447, 230294038, 342599089, 772808141, 4096882234, 3146662953, \ + 2784264306, 1860954704, 2675279609, 2984212876, 2466966981, 2627986059, 2985545332, \ + 2578042598, 1458940786, 2944243755, 3959506256, 1509151382, 325761900, 942251521, \ + 4184289782, 2756231555, 3297811774, 1169708099, 3280524138, 3805245319, 3227360276, \ + 3199632491, 2235795585, 2865407118, 36763651, 2441503575, 3314890374, 1755526087, \ + 17915536, 1196948233, 949343045, 3815841867, 489007833, 2654997597, 2834744136, \ + 417688687, 2843220846, 85621843, 747339336, 2043645709, 3520444394, 1825470818, \ + 647778910, 275904777, 1249389189, 3640887431, 4200779599, 323384601, 3446088641, \ + 4049835786, 1718989062, 3563787136, 44099190, 3281263107, 22910812, 1826109246, \ + 745118154, 3392171319, 1571490704, 354891067, 815955642, 1453450421, 940015623, \ + 796817754, 1260148619, 3898237757, 176670141, 1870249326, 3317738680, 448918002, \ + 4059166594, 2003827551, 987091377, 224855998, 3520570137, 789522610, 2604445123, \ + 454472869, 475688926, 2990723466, 523362238, 3897608102, 806637149, 2642229586, \ + 2928614432, 1564415411, 1691381054, 3816907227, 4082581003, 1895544448, 3728217394, \ + 3214813157, 4054301607, 1882632454, 2873728645, 3694943071, 1297991732, 2101682438, \ + 3952579552, 678650400, 1391722293, 478833748, 2976468591, 158586606, 2576499787, \ + 662690848, 3799889765, 3328894692, 2474578497, 2383901391, 1718193504, 3003184595, \ + 3630561213, 1929441113, 3848238627, 1594310094, 3040359840, 3051803867, 2462788790, \ + 954409915, 802581771, 681703307, 545982392, 2738993819, 8025358, 2827719383, \ + 770471093, 3484895980, 3111306320, 3900000891, 2116916652, 397746721, 2087689510, \ + 721433935, 1396088885, 2751612384, 1998988613, 2135074843, 2521131298, 707009172, \ + 2398321482, 688041159, 2264560137, 482388305, 207864885, 3735036991, 3490348331, \ + 1963642811, 3260224305, 3493564223, 1939428454, 1128799656, 1366012432, 2858822447, \ + 1428147157, 2261125391, 1611208390, 1134826333, 2374102525, 3833625209, 2266397263, \ + 3189115077, 770080230, 2674657172, 4280146640, 3604531615, 4235071805, 3436987249, \ + 509704467, 2582695198, 4256268040, 3391197562, 1460642842, 1617931012, 457825497, \ + 1031452907, 1330422862, 4125947620, 2280712485, 431892090, 2387410588, 2061126784, \ + 896457479, 3480499461, 2488196663, 4021103792, 1877063114, 2744470201, 1046140599, \ + 2129952955, 3583049218, 4217723693, 2720341743, 820661843, 1079873609, 3360954200, \ + 3652304997, 3335838575, 2178810636, 1908053374, 4026721976, 1793145418, 476541615, \ + 973420250, 515553040, 919292001, 2601786155, 1685119450, 3030170809, 1590676150, \ + 1665099167, 651151584, 2077190587, 957892642, 646336572, 2743719258, 866169074, \ + 851118829, 4225766285, 963748226, 799549420, 1955032629, 799460000, 2425744063, \ + 2441291571, 1928963772, 528930629, 2591962884, 3495142819, 1896021824, 901320159, \ + 3181820243, 843061941, 3338628510, 3782438992, 9515330, 1705797226, 953535929, \ + 764833876, 3202464965, 2970244591, 519154982, 3390617541, 566616744, 3438031503, \ + 1853838297, 170608755, 1393728434, 676900116, 3184965776, 1843100290, 78995357, \ + 2227939888, 3460264600, 1745705055, 1474086965, 572796246, 4081303004, 882828851, \ + 1295445825, 137639900, 3304579600, 2722437017, 4093422709, 273203373, 2666507854, \ + 3998836510, 493829981, 1623949669, 3482036755, 3390023939, 833233937, 1639668730, \ + 1499455075, 249728260, 1210694006, 3836497489, 1551488720, 3253074267, 3388238003, \ + 2372035079, 3945715164, 2029501215, 3362012634, 2007375355, 4074709820, 631485888, \ + 3135015769, 4273087084, 3648076204, 2739943601, 1374020358, 1760722448, 3773939706, \ + 1313027823, 1895251226, 4224465911, 421382535, 1141067370, 3660034846, 3393185650, \ + 1850995280, 1451917312, 3841455409, 3926840308, 1397397252, 2572864479, 2500171350, \ + 3119920613, 531400869, 1626487579, 1099320497, 407414753, 2438623324, 99073255, \ + 3175491512, 656431560, 1153671785, 236307875, 2824738046, 2320621382, 892174056, \ + 230984053, 719791226, 2718891946, 624), None) + self.random = random.Random() + self.random.setstate(fixedState) + +""" + Data structures useful for implementing SearchAgents +""" + +class Stack: + "A container with a last-in-first-out (LIFO) queuing policy." + def __init__(self): + self.list = [] + + def push(self,item): + "Push 'item' onto the stack" + self.list.append(item) + + def pop(self): + "Pop the most recently pushed item from the stack" + return self.list.pop() + + def isEmpty(self): + "Returns true if the stack is empty" + return len(self.list) == 0 + +class Queue: + "A container with a first-in-first-out (FIFO) queuing policy." + def __init__(self): + self.list = [] + + def push(self,item): + "Enqueue the 'item' into the queue" + self.list.insert(0,item) + + def pop(self): + """ + Dequeue the earliest enqueued item still in the queue. This + operation removes the item from the queue. + """ + return self.list.pop() + + def isEmpty(self): + "Returns true if the queue is empty" + return len(self.list) == 0 + +class PriorityQueue: + """ + Implements a priority queue data structure. Each inserted item + has a priority associated with it and the client is usually interested + in quick retrieval of the lowest-priority item in the queue. This + data structure allows O(1) access to the lowest-priority item. + """ + def __init__(self): + self.heap = [] + self.count = 0 + + def push(self, item, priority): + entry = (priority, self.count, item) + heapq.heappush(self.heap, entry) + self.count += 1 + + def pop(self): + (_, _, item) = heapq.heappop(self.heap) + return item + + def isEmpty(self): + return len(self.heap) == 0 + + def update(self, item, priority): + # If item already in priority queue with higher priority, update its priority and rebuild the heap. + # If item already in priority queue with equal or lower priority, do nothing. + # If item not in priority queue, do the same thing as self.push. + for index, (p, c, i) in enumerate(self.heap): + if i == item: + if p <= priority: + break + del self.heap[index] + self.heap.append((priority, c, item)) + heapq.heapify(self.heap) + break + else: + self.push(item, priority) + +class PriorityQueueWithFunction(PriorityQueue): + """ + Implements a priority queue with the same push/pop signature of the + Queue and the Stack classes. This is designed for drop-in replacement for + those two classes. The caller has to provide a priority function, which + extracts each item's priority. + """ + def __init__(self, priorityFunction): + "priorityFunction (item) -> priority" + self.priorityFunction = priorityFunction # store the priority function + PriorityQueue.__init__(self) # super-class initializer + + def push(self, item): + "Adds an item to the queue with priority from the priority function" + PriorityQueue.push(self, item, self.priorityFunction(item)) + + +def manhattanDistance( xy1, xy2 ): + "Returns the Manhattan distance between points xy1 and xy2" + return abs( xy1[0] - xy2[0] ) + abs( xy1[1] - xy2[1] ) + +""" + Data structures and functions useful for various course projects + + The search project should not need anything below this line. +""" + +class Counter(dict): + """ + A counter keeps track of counts for a set of keys. + + The counter class is an extension of the standard python + dictionary type. It is specialized to have number values + (integers or floats), and includes a handful of additional + functions to ease the task of counting data. In particular, + all keys are defaulted to have value 0. Using a dictionary: + + a = {} + print(a['test']) + + would give an error, while the Counter class analogue: + + >>> a = Counter() + >>> print(a['test']) + 0 + + returns the default 0 value. Note that to reference a key + that you know is contained in the counter, + you can still use the dictionary syntax: + + >>> a = Counter() + >>> a['test'] = 2 + >>> print(a['test']) + 2 + + This is very useful for counting things without initializing their counts, + see for example: + + >>> a['blah'] += 1 + >>> print(a['blah']) + 1 + + The counter also includes additional functionality useful in implementing + the classifiers for this assignment. Two counters can be added, + subtracted or multiplied together. See below for details. They can + also be normalized and their total count and arg max can be extracted. + """ + def __getitem__(self, idx): + self.setdefault(idx, 0) + return dict.__getitem__(self, idx) + + def incrementAll(self, keys, count): + """ + Increments all elements of keys by the same count. + + >>> a = Counter() + >>> a.incrementAll(['one','two', 'three'], 1) + >>> a['one'] + 1 + >>> a['two'] + 1 + """ + for key in keys: + self[key] += count + + def argMax(self): + """ + Returns the key with the highest value. + """ + if len(self.keys()) == 0: return None + all = self.items() + values = [x[1] for x in all] + maxIndex = values.index(max(values)) + return all[maxIndex][0] + + def sortedKeys(self): + """ + Returns a list of keys sorted by their values. Keys + with the highest values will appear first. + + >>> a = Counter() + >>> a['first'] = -2 + >>> a['second'] = 4 + >>> a['third'] = 1 + >>> a.sortedKeys() + ['second', 'third', 'first'] + """ + sortedItems = self.items() + compare = lambda x, y: sign(y[1] - x[1]) + sortedItems.sort(cmp=compare) + return [x[0] for x in sortedItems] + + def totalCount(self): + """ + Returns the sum of counts for all keys. + """ + return sum(self.values()) + + def normalize(self): + """ + Edits the counter such that the total count of all + keys sums to 1. The ratio of counts for all keys + will remain the same. Note that normalizing an empty + Counter will result in an error. + """ + total = float(self.totalCount()) + if total == 0: return + for key in self.keys(): + self[key] = self[key] / total + + def divideAll(self, divisor): + """ + Divides all counts by divisor + """ + divisor = float(divisor) + for key in self: + self[key] /= divisor + + def copy(self): + """ + Returns a copy of the counter + """ + return Counter(dict.copy(self)) + + def __mul__(self, y ): + """ + Multiplying two counters gives the dot product of their vectors where + each unique label is a vector element. + + >>> a = Counter() + >>> b = Counter() + >>> a['first'] = -2 + >>> a['second'] = 4 + >>> b['first'] = 3 + >>> b['second'] = 5 + >>> a['third'] = 1.5 + >>> a['fourth'] = 2.5 + >>> a * b + 14 + """ + sum = 0 + x = self + if len(x) > len(y): + x,y = y,x + for key in x: + if key not in y: + continue + sum += x[key] * y[key] + return sum + + def __radd__(self, y): + """ + Adding another counter to a counter increments the current counter + by the values stored in the second counter. + + >>> a = Counter() + >>> b = Counter() + >>> a['first'] = -2 + >>> a['second'] = 4 + >>> b['first'] = 3 + >>> b['third'] = 1 + >>> a += b + >>> a['first'] + 1 + """ + for key, value in y.items(): + self[key] += value + + def __add__( self, y ): + """ + Adding two counters gives a counter with the union of all keys and + counts of the second added to counts of the first. + + >>> a = Counter() + >>> b = Counter() + >>> a['first'] = -2 + >>> a['second'] = 4 + >>> b['first'] = 3 + >>> b['third'] = 1 + >>> (a + b)['first'] + 1 + """ + addend = Counter() + for key in self: + if key in y: + addend[key] = self[key] + y[key] + else: + addend[key] = self[key] + for key in y: + if key in self: + continue + addend[key] = y[key] + return addend + + def __sub__( self, y ): + """ + Subtracting a counter from another gives a counter with the union of all keys and + counts of the second subtracted from counts of the first. + + >>> a = Counter() + >>> b = Counter() + >>> a['first'] = -2 + >>> a['second'] = 4 + >>> b['first'] = 3 + >>> b['third'] = 1 + >>> (a - b)['first'] + -5 + """ + addend = Counter() + for key in self: + if key in y: + addend[key] = self[key] - y[key] + else: + addend[key] = self[key] + for key in y: + if key in self: + continue + addend[key] = -1 * y[key] + return addend + +def raiseNotDefined(): + fileName = inspect.stack()[1][1] + line = inspect.stack()[1][2] + method = inspect.stack()[1][3] + + print("*** Method not implemented: %s at line %s of %s" % (method, line, fileName)) + sys.exit(1) + +def normalize(vectorOrCounter): + """ + normalize a vector or counter by dividing each value by the sum of all values + """ + normalizedCounter = Counter() + if type(vectorOrCounter) == type(normalizedCounter): + counter = vectorOrCounter + total = float(counter.totalCount()) + if total == 0: return counter + for key in counter.keys(): + value = counter[key] + normalizedCounter[key] = value / total + return normalizedCounter + else: + vector = vectorOrCounter + s = float(sum(vector)) + if s == 0: return vector + return [el / s for el in vector] + +def nSample(distribution, values, n): + if sum(distribution) != 1: + distribution = normalize(distribution) + rand = [random.random() for i in range(n)] + rand.sort() + samples = [] + samplePos, distPos, cdf = 0,0, distribution[0] + while samplePos < n: + if rand[samplePos] < cdf: + samplePos += 1 + samples.append(values[distPos]) + else: + distPos += 1 + cdf += distribution[distPos] + return samples + +def sample(distribution, values = None): + if type(distribution) == Counter: + items = sorted(distribution.items()) + distribution = [i[1] for i in items] + values = [i[0] for i in items] + if sum(distribution) != 1: + distribution = normalize(distribution) + choice = random.random() + i, total= 0, distribution[0] + while choice > total: + i += 1 + total += distribution[i] + return values[i] + +def sampleFromCounter(ctr): + items = sorted(ctr.items()) + return sample([v for k,v in items], [k for k,v in items]) + +def getProbability(value, distribution, values): + """ + Gives the probability of a value under a discrete distribution + defined by (distributions, values). + """ + total = 0.0 + for prob, val in zip(distribution, values): + if val == value: + total += prob + return total + +def flipCoin( p ): + r = random.random() + return r < p + +def chooseFromDistribution( distribution ): + "Takes either a counter or a list of (prob, key) pairs and samples" + if type(distribution) == dict or type(distribution) == Counter: + return sample(distribution) + r = random.random() + base = 0.0 + for prob, element in distribution: + base += prob + if r <= base: return element + +def nearestPoint( pos ): + """ + Finds the nearest grid point to a position (discretizes). + """ + ( current_row, current_col ) = pos + + grid_row = int( current_row + 0.5 ) + grid_col = int( current_col + 0.5 ) + return ( grid_row, grid_col ) + +def sign( x ): + """ + Returns 1 or -1 depending on the sign of x + """ + if( x >= 0 ): + return 1 + else: + return -1 + +def arrayInvert(array): + """ + Inverts a matrix stored as a list of lists. + """ + result = [[] for i in array] + for outer in array: + for inner in range(len(outer)): + result[inner].append(outer[inner]) + return result + +def matrixAsList( matrix, value = True ): + """ + Turns a matrix into a list of coordinates matching the specified value + """ + rows, cols = len( matrix ), len( matrix[0] ) + cells = [] + for row in range( rows ): + for col in range( cols ): + if matrix[row][col] == value: + cells.append( ( row, col ) ) + return cells + +def lookup(name, namespace): + """ + Get a method or class from any imported module from its name. + Usage: lookup(functionName, globals()) + """ + dots = name.count('.') + if dots > 0: + moduleName, objName = '.'.join(name.split('.')[:-1]), name.split('.')[-1] + module = __import__(moduleName) + return getattr(module, objName) + else: + modules = [obj for obj in namespace.values() if str(type(obj)) == ""] + options = [getattr(module, name) for module in modules if name in dir(module)] + options += [obj[1] for obj in namespace.items() if obj[0] == name ] + if len(options) == 1: return options[0] + if len(options) > 1: raise Exception('Name conflict for %s') + raise Exception('%s not found as a method or class' % name) + +def pause(): + """ + Pauses the output stream awaiting user feedback. + """ + input("") + + +# code to handle timeouts +# +# FIXME +# NOTE: TimeoutFuncton is NOT reentrant. Later timeouts will silently +# disable earlier timeouts. Could be solved by maintaining a global list +# of active time outs. Currently, questions which have test cases calling +# this have all student code so wrapped. +# +import signal +import time +class TimeoutFunctionException(Exception): + """Exception to raise on a timeout""" + pass + + +class TimeoutFunction: + def __init__(self, function, timeout): + self.timeout = timeout + self.function = function + + def handle_timeout(self, signum, frame): + raise TimeoutFunctionException() + + def __call__(self, *args, **keyArgs): + # If we have SIGALRM signal, use it to cause an exception if and + # when this function runs too long. Otherwise check the time taken + # after the method has returned, and throw an exception then. + if hasattr(signal, 'SIGALRM'): + old = signal.signal(signal.SIGALRM, self.handle_timeout) + signal.alarm(self.timeout) + try: + result = self.function(*args, **keyArgs) + finally: + signal.signal(signal.SIGALRM, old) + signal.alarm(0) + else: + startTime = time.time() + result = self.function(*args, **keyArgs) + timeElapsed = time.time() - startTime + if timeElapsed >= self.timeout: + self.handle_timeout(None, None) + return result + + + +_ORIGINAL_STDOUT = None +_ORIGINAL_STDERR = None +_MUTED = False + +class WritableNull: + def write(self, string): + pass + +def mutePrint(): + global _ORIGINAL_STDOUT, _ORIGINAL_STDERR, _MUTED + if _MUTED: + return + _MUTED = True + + _ORIGINAL_STDOUT = sys.stdout + #_ORIGINAL_STDERR = sys.stderr + sys.stdout = WritableNull() + #sys.stderr = WritableNull() + +def unmutePrint(): + global _ORIGINAL_STDOUT, _ORIGINAL_STDERR, _MUTED + if not _MUTED: + return + _MUTED = False + + sys.stdout = _ORIGINAL_STDOUT + #sys.stderr = _ORIGINAL_STDERR + diff --git a/proj1/学号_姓名_实验2.docx b/proj1/学号_姓名_实验2.docx new file mode 100755 index 0000000000000000000000000000000000000000..6d60e7527a3e7d4839a93e3a69f2d59451ba43c9 GIT binary patch literal 29807 zcmeFYWmqKJx}}>qg}b}EySqab6z=ZsP636xySux)ySqCS?rxX0R^NU1>2vya|LI@d z@uWm1Gvmn&-ZAF)jfnUm4FZY+00%$<002TjNuK6=8V~@W`SlVR012!iY-85|UPALnPGXqGQN8e?<4rDAW*$(A~P-rtT;wHK} zR6duEYD3?`3PVA@=3{@#!*w7Q97%ja9}xBiHLmeAR4hB6f-SGp*7l1K{#%9WXSw>9to=rNJKj&fp#9jgEbuZU=mXh z-M#hT>fa+C7AXAV*n;*)(_EH&Z04t8kkzH6ffcusM}VBANlF0@#;(t(=Xi25uzJDj z8tdNzSOhAfFJ)s({$g?tDoW`b*O~_C}Tt^mKnc{$DNs zA1vU1`|0JeveF=o2!WS=pM>Kb+-p=svm4qXe`F2cfrOb}ebeA$OBz2nDMIPy8-nnd z-Ht|>g%{I0a2KeDSX@8CupOco)LmE2Xhv_%IgW)hn$iA+Vl6Ej~ne zLTR*F6TIzM;vSQWR-n<8lEc-)E`pty?$X#LRNqj6$8OzSI@A%d#pEA z8;(XW$9LO2B4#A7=Q#>348c!h$A?X=x=Ek7=d`+`3u*c+KVrVE_#GFe*7aY?1jH*Kneo@5Wn`3i;cY@y`hbPlhxNe_}2t@rmkVLCWhv%qxWZgtjtPIuTSe{Y?X z4U17mZp0zB6mo7xv?tg*`sp@nlYZcqN+%v{ARGWW5S+x~&85yDOB+c5u7hAWnH(#s ztpe+`n7*roDQ{vTsx2T@nkjCJB7Dv*tK!zf(Cum_mvR(d3_V2uBs_Mi`OPu#7fCNV z#|8?T9AmYwAO%Tab%wwbf)hD7`f!2ZML+R?%7LAi3`VXa6muR7*;L4`vZ9~T8P!qIQA(1Bruy?&IKg-a@r zeHnASdr)B$Sh>(h+ww}`e#NZwf_P}KJT$HZx9^b2Nu-w@v4PK;<{kO8doq2L+4#2` zEpennHy^!u-P{0E?W+_T9+<@efv33z zSUZudvC%#|W3b)eqJDio*6NL9vR|Hd1d!=hXTQ*wJHCw#r#<)T+HT?yR@0l8zSc->&A|<-@)iZ`NxQ{0?-c%Ip+_>G>Y+0%;mX7k;R4EJgHVlRe4~4 z9nd`)BQl9GALaoU0g)y>KOJz2F0^2NQw2@50WI$i<^{Q;hctApu?YZc!ySzBV5aU# z^JD}jWlRN#ZilDp{8oYGA=twza!BgI%B94Soi(H~Z}LMi?^Par|Fy_L!Ew7(m`5R1 z!;&$|lFHOeqY^(}NY3`6gsTk`vFA&^ttI)}GnN91yO(|`bQ)LBcW;6Mcxxep&RtGP z2Be+DHZX-n#F|Up>v?rm>vb)NGQHdO=pdzDPbcDgPueXGq;;>CzGrG+N7^a^I=0<} z-;x|SB+DRE3J$ImxSY1Na@2(@ocqO&Wzm>i8h=tx{!fk=DQ$wJ630NJ*_7pEEo+Bl z<|=_9bredM@Og&&tpVbah|uosd4`ivuRnO}{Xyu-?dGSO=5m~KLF4N8{7IENtI9@Q;*B`| zm_d6r&-Ddt!KS0<+!8MBdio$cbQ>7STo~cK9yb~)-8$4PRHwdnV7wyebT~G*M$4M10X3|eGdxV9pg@nA6DWEVvf*iY z${SdQ+~hlAW?Aq zo!P{p1b?Wy>~i>{4;&Xaw1KPnL&YeDF9vqTGL|pOP6ITknl7JWhWHtblXE;Q<}ooG zmCx3-;e#`NPqnim;)yMvWudZ~#G3$#2}h-p7~7YB#NdZKZbCb+2GP>0mtSO3GlUY> zV$3u`cUPk%8eqDrd$N&Uw8+AEmEhBAz!Z6N^9yk{vOTl~&$MVr^6E385znbIZD7Sw zq!Lpma<*BiP#DzS5(J;TjO~*MPj0p>T=16hz8rCm|N0~k^OH3GgAow*dNiEV$f)J* zgun0F9d@jZ2~`A%V#EEr1rQmxf|5cuXT{_Sgd?6}iI)E>4E=Y(l58Rl}LEe8QBF%y%A```U1tuUSuOvVR zY9aSq-qC3LKCcrlRQMW@Sa`bVJ)v*s5-2JCAE6Jbb6Q>h%JETTEMOdvvj0vfxlh`Ehk zbO||Et9f%97Z8r3Ps$Rvp#tPppJd!M)KZe=d1W}%v@1XNbObeGNcyIwUaG%j9d@_fMMGgusHTOGie`qbU2-Rm~E zR~w&ynp(XXZ*;18HE3Q!};zF%}z zR#Yfb)0?|I0vre-D;k&l;Op$q2f$Umed#$_OrayQ5^>qOWw$M-ed#MQJOuo*>9*E^ zTVdu~yqj?R?)|^Z>meL5=@x$Mi@lpRxx0?(9Z$@{o+i=%!ssb-PCx+;DHI}@u_EGO z4BJN5F><;;sJOyJ?L7}tQmbuT0$w^^+FI1Pn5W*FxAwy2i_~3da{-C(Snzk!%_Bw@ z{0TmKfrcD?0YQv1(SNlL#*K09>X+UuEW;M6o=3jzA8l-Kb(IXDwUJdj$EKGNuO|kc_es#2uicH|^M2c_9#jI;`Z4Z?T_jRE$X)g@(BN+h ziA??~JR090c7^43u?%p~pAdgl_h%Y^S05kLzcU|@12u&~B9 zCrJ3^(|TmBv&p7cShPvTIi9j~6bZ5^E6_~0*2cXhMM#W%e}?u5%(qm?u^LRI&5hX^ z8^@eXVLrOaU=~(mP0&06DF~3+^_nL;tWV4@3~rqMp-7!16l-=MD=Jk$-;rhwEjG}6 zXRT39$JlJ*#v*g=?UpB|_LCM_Ridz5gc-kfqQJe4{%q3Z!q4qqF?Siet7}!Gj{m*l z@oekiaKZZ#{iyBqmEUt=?RfP&dn~cZtYYIq$#HK!Rzgl2kp3M$|HXMZi8%8%?>jxd zoD}(|MhkmMK@SwY3Sa8_U{}igEb_+v{`iKLbZ3i~ad+$E+2Sb=UiV|ZGXN7pSjB}_ zewvlis+~02E%%%Dd4(KJc3ixu&iRG}N4*U5NES0xEa-4t7_ffH0*Z`QtUO%-_OQqx zk<`vK!7gO{TYZ~@T`2F&sEyTX!KRx<4I8q{=}>@zV>FT^B!h>A1(q%{eD!{^t{-NG zR4iJd(m4w_S$5Dk3;4%6Yek3qg!X$>opav>*`I)c?Sc1?i--LA*{!y{@-3N2HtkAJ z93*h>Q&$o4RTSbsv4kBe69pWh^a-kg(k+V6v}(ztuR5-%eTtZvZv1U z9UwLKYY6JQUcuxh2(6x2TvDH>2(x6IB`9~FfmYLYrZ`kUp)nZrX-}JZVoppsT)_z= znVh<}ZKw>9IVK1jLSYmue#L`NpL~B8w|K9B6Up>)u)5kNK3d|??sq8g zBv?hp&7*PY`B*(tnyjgYDep1Qp}t{E8dlm6q>_~qzWBUFVxr7_a&-B zK{cwpk{}g;Qnk&O@HHnvX46w8tzDeAC27H;NYe5Nwh4&aI{^Lg-eGXPx&0uA&!NE| zxR+Q+Pa}DyPD4m|)vwQ%^NlG!!U%(P+!0I6t94;-gC*k6C9f10q=o5N$go4UncmfnUPcZ z2d5AGmYV2k;Ncjle7=|?a!8U1Om}}7wPy!n1)Xsq9it`CC2KYv(ys_wrUD0un*LJ; z&(7dQLY4dy&m2XV(`fyEnD2ATh3Ikto4t(XPGL% z56+9ckIs!gn?83xaqy{3FcRsf_3(4U4rqj#gW!;3&?fghZO2IqeNNqC9Ri&&KY0*a z$|%wz-=lXDo*$~3UiqDRT;K1D-Zw1#eY|k$M(Iq{GM&P*f4wu2Oze*Z*s5pgcVjrH zKaTAmupB-7Ji3*GDayHJ&L2Lkp$AP5)ElLtL}Nse@&CZ_`=HOeu&)`50z}rm3~##g zx!R*ryl-eszN^}-4gL1E=s3z+fd}k%M($L^t4>A-03$yvGAD5&b|ND?Z0=h=*c&Ai!C#kuoA5h{s}SM4G>U7oqGX@ zcdb$3)EoCmQv?n|`n5R(Z8XBwN|mlp!=OKlFizF2l=J^84RuKn2o2zgJF3=f1#k00vkpK8XXI9S&hQZ z$Bw(O;Fd5b&xI!%3N95Fc^x#QnsqDDZN#FWE-nHr7Pv;!xPrZH$@snB>ngEyNu?4+ zXfQ@8RgXh0X)z$11*NCq`~4PET1vuDMF%|waeoZh4Px=_%0^$kWLo7qVSBHi2P&u8 zP`Rs_u`yDwlfeKr93qy(U7XNSd@D2}khb17hQ-SOc}RBB?LP1rhI*7i?Ku4{2%?^}2`ihLMK5X>1lhhpH*IRN4VUZ|B2hBDmf zaEy{gDtOp<4i-~1)m{|5+hx%Vk)NV2B*rel&=VK$-f>|~Ubi=#U9d{0C(v|Hkz2S` z-Awmxn8};v&wZDqDx372?_}I&i9iWE1INpT+eB^sqFz^sx$g{kZ0T@!(maaXa%XtZ z`-|Vf!#h~cO+VBfKj|>M4vWx3hb6^R|IRt&P-Tir*+osC778RP*e?`(-}Q)L2FC$| zTa`>~_xK{KCeA9QBwn1^{Qxx8+u6^rB>q3=dD)xuqrzW*5djAPZ~%}%e+Lx*gnIuT zQ~bXHAJA7=|26vm?4>?_Os?;*uv+3xc%Rn}DVt|LAN%(_Z9Kef+=y`h-f$-HyGOa0 z8;W{ZfZAk(=jB2S@7O|;r9b?1p?q>JN4P2=xh%Jb%E%_|sLTTm14%VlnK0iHKCq&8 zEbIWf-tUa`Cws=Xa_H3f1(6NnBeE(%DCAPO>CAX3X+h_C)Ju^MI;w3+6x!xYkLt5w zW>s{`xzpZ5n+M#t0gVKsxW2ei7o=!8kt+pCU@P2!%3lhLQ+B2i82lMN=geDx7*dNu zaR1#>+oL)mV%iT%9if?E?eIRhG6!6wDh$)DUYuS%XTo5w!96)^L5-82{_^$-s!udP zs@Yzi%uxQj``f6XD{Xa6wiq5W&!pB~J|p-9Vk447H*3IV#Es|SgA^_5>rXV--`19N zp!LaGLwVjlQ)L1@z3X4=Y`)0(&kzdJbTf{B1 zPh7K4WBAGxx#wJTn_c=x*!1*GR+rEB8_no1$K_%||ge=~}v z^v=%0P~{`wrW+3(f~N3+7!f#MbbDC5A#<>2`T zJhKT&8WY5B!n5vpBlxksZuvdVM18Q69EI*odVT^0HUmHi6xvXo)4Fg<59tRft)_)_ z<_rvZ^vq1N+O)n;*M_}V2xxojQP@yx%K_Q)=Ut}nvM2HfIy&m`=~2*SeH*}@$}Nf! zRTz0%2b~7R1j(fetOT}|PKJtwm^N)7DbjOsW+0rMq}?(56At7Tku(im4UJk2Fu1ld zFZZi4?W1p2xu@Z(vi_#LgZWn`<7CLWF1F~di7dQ!8!;0_K&~L&P&+aBVS$K?Fuz() z*wQTx)mYQbm0-wEe>;gkbT5tE0qhvR?L5ok9qO-p&|sEvWCU6)LSpC?>p~f6u%I$x zu0UexPZKL+)0h;Wn*@dwg*00FqR(mwd%No7c%zqZ@YX{Ka`1VM%v@kR{!=!hcUNGC@E`Glz6;bxA+X zC3@1!Dz6U*rYN(#YItI{lL&8rjwxb%A&=cb-@~Npv$0m7dPtMA;x&x#@HtU(8xohhj*Qet(cw4DhETAluKuk$2?1uQ^ zP966sm80x{YyGkV9`(o^?Gs`2o7R#`U(hF^;-#3i&wrT^ALz@0{l&iD7yJK8h>ffb z|A*-JU<8F^jMT)fk$C|`A^Sja#gGQQU!`z1whILmbbhtZdbhpJ(rIepOIV-RL}TV* zuXWg3LjUaF4ZmP}s7e|dSp66J_%-!aO5d?73zsWBV`@+W9vY@Xkwq(AR4`R7~fT>p@-1?>5wMX};9^3T1e zeh4xsC726rYn~1l%P?;`K`VW0_B7b5trhW3w z;EViaS^qygf0NIKocrAt|BHO`-B#kX@6cD^zfgOLb-sJDt-<&QZUv1E30jIE(&7j*E%NCZ+oxd=E5+ zdGeB?w`-OQhI=I;_Yi%_r&7!hO6~f6j8EL-M8nyPFO#e~)sNpw=*D4$rzrYVPVUED zeV$DBlBP-i*wQSjsS>wt@GTX>DQ06DU?znAxC?cew>2p4QcJigmdQGX)D2(T4JNQUP_;}sFE*Yg5Dtm0x z$|z7VJvvw@`9%|Q9xSKd`k9&;8c&7nOBkyOQcxAh$)o)4sVt}`na4;zfeT}0^Nt`+ z^R8FiLrTYG5F;5{ot?-;#Dm4m!%TG+oB<%T>!CQy=jF*G?<+b9ToWJ^pF|J#N$9k% z8)8tN#L@-Eh-u9_VwBXSySn&Fi-I{CD>zv06loJh-oHv^0IBL}w4;_+Yv_WINeR zuzLG-AP0rHx}QdJnFQp`ah;0316csOjNeVI!~0!3GPP*c7lKV(g{m$6^rCvH+vL?g zOZ06wjL_TIsfkKwolBlGfjv|(C@#jZCsz>H+=7gi=CDf#HW|hS!I_j+X&*Io=nExX zcm9S0tH?cuR@sTFrZRUDH7BPHK?QkT~3X=K?J_Cc~VgQJXf{i?l?_<;1R&`V<07o6Pan^$L_2zI2XJV z>J5B9-`X$?VQ=zCRO6;n8og~f*XC{%b?D7w*&09Zs)ASfxu$@1ggEIccl6*(_3C{6 zx+IG@sII=9QC5v$XicsjhCnEaG+NziOc|qjt*V1Oi4e6H|3lFhlW@u|!=8kMT#*v8 zq4yb%x@|DVi(j3@4rmgbLx`kDVeUOG%FGO~NNS$y!b zA=}k~Z}aNDA@{qAF!P&Jcv{;S;VpisRCLAQDSzA-+-IwGRU=pj{?Rs(V(+Z|enIuf zO0FU1pk)jznJS3qRvD{Uzu^m8&|^3&;LJ~|J5+nvBGk;=wCYk}rIoM)c}Cn-i91Gh zz>lm|B+d$(d;f=c$6#FSn?I^23=ah>`G}JTTNe1X696cOR|gH&K%E(Z^TN7M(!;te z@Tby%?W}?RMkeo7T{5yjJumdma|E0N;{`bl>M;c;{L(xmrcSw?pVD7iTdzZ8>7V&l zqppidZpF_mSXHLPEWHm27(zGifO`pe$tKqCSDQi-F0_(H-d)`Q28|f?I zWJcn%vZuS&01wrDRj&rAs0BX6MyqX!7jC6LH-4Y@=V10XwMa=crQd!d?`D7kOMLqF zFu8AC3zI(6p{2gFP$F&dr8P*C@jp}R&gs`IkQa*;X0itD6ZI)2e5mhgiAm^Q!vfmP zebpCN%VL9A(&K;XKphAf9Tj03#)yU2X&qv1UH34+2BTTPT{&%JZmD|TnWWimS4pqT zvu9P|Z0{3$#0A%JavQ-d#epdm$9KPXyZMzv&I?YyGc<5b%A2 zrv?)h0Ql?vb^B*(vobO?)1x=D(laq)q_ed)3Hc!>4hx0#*Qa15B}5be03eL7^<~Jf zauCqvV6pb~^vza6&Ecyna{TKKv}ug*3IL#tNs0(6x~89RRM_uzE(C&0{DVv0|9>%00Bk%1rA99fJKl1U=c(ESOf@wOn(Ug zPyaK(Oc~E3oDt2-g}+nyYfbdn#bqx@fIz-K8U1K$BdjfMOys7!sk05AzduwU^yKs> z79)LUr>VD2!(}@K)`K=>!Q1625aI&6818juBRp-|v&g2p7MP&}&-u#RV*v zxVghNTDW|k$>DlfqTDTV$wD`)ncq)J2=EWi&xlwLAKTF|Tg6!QP3#vDqoJp4*ln)j zFl+hvQNA`e;@^D-f@#XT!k#It^y{9%f zNd!~eX?Y*L^=K%Q+wt7nYr{CbdWtD=o;lr7K#uru^U1WZEuCD^{%?I@+jUb*E*I0 zWu1Gu_biy-rf1dY)wJ+g0CdYrUKk%L5Z`TOI##Uz=-pbC+g%ki#uW zbj)VpE9AuVB*p7WLo>!2jyTA-N;4@r=huy12GMXZD);y+KZZaY7Jk~Z0+XG~s&nO8 zK#KS#vHkq z%vv^nQ={qgC?ZYF1SqvP>ORimB3A2xnFBf+98g-$$kjkuA#I8gx7w0+osdHht|rV8wJ4yZohKHGh*gW}ma{3luP@zB_gOgV zZ7n!N8GCqlFClnK!GB?fON5y~!5fmo$DN(Jb6T#y6a3!u2#>0ng>1K_?55dFp zQ7xA?K_hav>2f;NPGO;|^WGTqDnUG)h0c-`$X$c2x5=ecySlle`;_4+Xwg-3XB>= zF%IU^S0oH=o@mK5#A>g>2w-1Gpoe1I?!L8o06p&dgf)&D*r1PISWF5&E8hp{T_$w6 zA0;ik%#0F``0s79p`lN(D{uR*uI(9h`BD6}l~nBy>_jjm#Csx$#V}qp z1zX5w7!?%R5d)hf%Y;>QAF*)8sL@}N#D`Ev*$>Lk=mte(B!@Df5>0|%y1C#HqG7GX zTTQsd8N@KqUZ=m_ZGXZUyLq#$5YjT@_$4KdO+u6_m-j5<@2_#1+oa5@+C>&@*qpY$ zC4g09=%MKGx|EtL_t^$9TK#zbI&RP{NhV59q;j>bs&ijH^egVqz3uY0<`J?Wk11VV zBED^G(H3;sC$#c<%%D*{T&LHt_T*G%v1S<# zh=TH5dyLzgTGho>4Mok0Q)**8kG5#PJgP?O4^09nvns{w%R?m12r=TpTGl@7v8oGP zi_qfL4g#W-{uVFqAh&eH6k`kSzRep3o_DfRZOsK;!OT^k43g_}ne9)9PS2ViPDS#x z18xyDXKTOAAmvJOaR{q1)6El11M$lN6TSj?{hogk@TQ0ysyD33_QLP)RdS@s{m^DH8UXt5wOyb|)tA1AVAiJbuj?L&YB-HEZ*3V1F_K$V$InTUI zOCpWPWh38z&u9pgPEZNjTcS{2bN%7NlUN0(m~ki4t~$Ulp;WOd7=6|&lIY(R%qXwP zd%L{WBBQ%exp%@jyF_6^}sf`6`!SJ3Wg%J(q||HJMPUBYZ$yLiHg zD1<+~Em{39+x!#9?>Q#fz8JP-J(v_eEfaULu&+O%0mELL`ffi#q;Wc6Bp)vp-lArE z=TQ~OvaG!RCtcW%a)nET$uiO!E1)(d7D$V~{#1@cXi!L1^v+ZIUq(#VhXM{i%34PDt1q zX1P$Ii{R zHWRX~@>M)EkC?P^^Qvf=B<+63b33b2aK!5>(+VQPN%k^;#sZdjU_ z_y{I)*U~*SW_0@-O@M%%E@>QFJ<~|;o??oj|e7*lsd>K7CwuVTVy0(a z^i{vor%f8u`@@Qv%ev$}x%_;ZG3(rLZHj2YlY9Bue#}4dN(;ArbjFx|EY{SsJ5_jF zvFMJS$s1_v3yqFlTOOwv4>81L!MJrp)MoldHOx8R$KSUJk=}2iX%$z0NghHomoPc9 z7tWRT3>^zJ<(M~(qTVSSZ}ePkcluQhBD*~=a3_UWlG-<9;SkMe8)EYYGH*z1361#A z%sBn!6RoUYo6`)mF_TZM;19H2B=wCKqlxE%Ooi`eNsF4ReiTJ|*1K7Hd1E;1nB$@k zi#zj_vhQOH`Q2t5gI>S2xr*FlY#P7+O2e(qjkiN$vuKdN% z01}!;+cMgyP=$yHuh=Ne;5;rbc7SsQ{L3JaB^zZ$m|}P!6IuRH-*4Ac%NVyhUfY&p z->Ri}a4LmNTJlf1!!$#7Jk|WMd!q`5kmm9cWS5JTXnnt%QEC{ABph!uT)5yezOGTz z2wEqZv`X6*xv{Fg>!VR={_=)^oyk1tDUdlfQ{!yq^iEN(BcXm(=N@nK0osy;4!4o( z_Hu0a^kK^d%xOoMoNFVUVXTM5I0-J0;K}H|8xT6=lq05?jk{IowJm%QxtqJ!R%@B) z5E48Ztb5v@G7E}UD@taC*zg+Up}U+(92wVaOP zIc9Y8kSK9vw$ectu^QIUf+OcccRWZjmv78T62-fSPf^iux39ep+j&5u9hS@UupYi7 z%=h&_WWvb;*K&l}DIXASgI4Y2La-P-k+T7|x}t>_7K~hFr*|#_J?JmT8P@QQb_-u9 zkoCW9IBa8FcqA}b5E$ny_8(b4JL@#YcEqqaOi^s)_L)mN4%EAZCqG0JofJI(GK<>O zuh|>7M(!@@HI*;WHsX_ypTDaO{_}S*<4gwnU&a36i_bTndUs^Eba|hKm!&?|vR}43 z2omgi%f>bKKgRYL;vIXapw~%Z&aKH$DphqAAK3-}@{1hY|G!4Kyoas6vmP z8$IfVon3WX=r9GgC{pmtqo>7_gRr$Og&4B8yfj+Pq+Qi06wh(ogb{UH{ z{VRWACeJ+c>CMe%b}DKzmhhS#U0VJ3-1b#C+CxWSJ+x8no`7pPy$tQYW`!LZxr3`8zy}XFRVj3$OB;j#^wc?)1(G-cnkLz1kfg znrE+~<(#%XX`2&nUGlHbg1hiItL};5+=w-YpktHX8z9vvb zm5K&hDNtyNsiV=}aK!XCWvCu$$m#LU+bv#@BKG@_A^SdCAWJpgnsGs}LiYUKVA(Q5 zgGgq2BUij7|E+D3!G)3Izx{w_srtbvweLkOeQru3Y)L&-hIml*@21LxMDEa{2Q~wq zq>m*8{Bz>-Q4)!>P;kX?;H!U{+^`$1BlxP~F)ny!eUOqH1^F-5D8^JGbwkF_Pi#>@ zmKqwi6Vb|7)jEYqVkei52e>vR!t>?5AWFgQkG@(6jH}&36}La{UX=l~mybWlV$;uO z(ZJntCAP%8nG6S6%bdO3(uXHKkuIil4=%qdN83}SJ)Q?4$;r4c^OuT;uW;-ls>cJN z>$`?fpDpeH=hd8IkR`fqeOm#49Utw~lFGL4+)cHx1E4Ci>@A zMwn4lokP1eZF9{)7t#E;*h$rP);%Y=K1RIUO)#B?eyBz0q;G&?Ghtj``aVJNfG3?LJ%Gr-Es+nkcHMXdJ0 zIPAQT+UW2@XPVS>edxBucJqYZ@N$2};#Zqr77h6Hw%n%ye)p8$zjB#>)9HU7+~^+H zIs0ebTYfx3hkm3IHDhEp{I5#>uT8D%5M)TBye_oiz0dgka?`<+m`U9q5*eWXK~*r{ z^IXd(%MquIq`T@@)aCIVn%#TYq0hf4qbL^OI4ez82_{faQsy?6GQMhXcpEo`A*od< z9P{pJ?}N;nQ4V(X{MyaX+a}ST+M;_~jPG;*+OP;+8}%2UBAyZj<18G$#CHn@=d zO?TA7ehvE9_hmaOFhdGi+B|7ALmRVJ!W|Ury2f29U)7r3SvK%XKWr-5>?Qrqd}j`H zr^ze+R}+7@7_)tc2K-gHVM4pmkmR121z&_s3gXHf8jH+laR-D4%zFg(-ryqXUzcjx zFb(?Wt+KOz^=lX@852vuf1aCsmgxQ=7%1@K^7s;4y>=W>-QVRU4m`b|)kzHW=YNXi z)#uwDjSMZ!5an0a36K<(6R8x^_4|*P9OlzDfxm+R0GZhTmZdtH8d(|9|NZ#)vBEPo zX&Yp5oGz6U7mRbRyVIBLwpsp(eT@EpWe5kGHU3xTd|oPMSkb=n7K9FD#6F} z9|6hsCc4yZaX0M{me?Zc4kFLqkcqe_HS)i06KyIWhI|8l5wYG$M5j!!R-&2>GNPHf zjVGyQ-zOS`DkLfg1;z*2N>(~qIB_Q(WV!y~XQ1n0z>&{)-B@JV2GfZ|dha*YpJOaF zx~!`>2hSOUOE!&YObbr}`qun)tO1^p2Gz2@z4X~N;XEt>6Y;0CDYPT+?Z~mcpROXi zBN5Ixq(#TzVZa8a4~TAQ_^ESShPBn;6rlRjzaWK9)H1J0-r~C(n!rl+c-v1aWo)+2 zZl8+ZTb5006SB}YUvJCz+9)nzb36V0LT$0DWA3={B*I%dGt^^c| zz*lHJ0U4GfNd=^|(2_dSiVf{iKVcdeg4$hQ_QHyyW=1;5$sfHrTYNtEH>coAsX4xd zDNc2L?p_9E<#yruJihg)Shv&c`g|VOZF#@X!YYbx*Hz)aU+>S<+3?=G`d02b)Z3Ux|NK8C+8u`{Tmiq zD^n2~)xxSP;uk_t9!MIp2G_cK({+?^zo(OayC3mr|8 zq-*U3DnFmC1zk+O$Tgnfx_Y#j8`sFw_t7>U!Jz|rJ+|G_;gP?BLiGZ9ZKZFqAANc{ zXe&>dY{edFE#veQL0I*XX}h1`DGb6b{Eo4hy|ejt2*#_*f-KC!y>IP^VvhqYAm3U?5(u9Hu8D}ZxsP(iT?2xDUh~}sFGAtxe#w63;Qy4OR z_J@SAh;R~k#8xgV(W%#KP}XzkLkk5~=8ZN4RtW^C1FVU`X=0bMxhzHuQL&Y40rrL= zyuw4-N_Cre(NS}W?7#QWVD?a+BsWSKpyLvc%GTq5hPln?x~MOfO2amaIqtCKs^EzQMb`lptHl~A zsrfYOF7uzwg#_dxpJm&pNov< zcz!AC0SG8zdoUTi+;p@YuLrx@TPx4QH%^QRxRsUh()9|>!^vvLSu}jjAhj+#-Ymva zSg*rc&GF8KpMroanOwq+kH0Ts`R^}LS4ClsbcX`~{8|3(j4B5sM@KVjlYdSo)UdEw z7fXES>wbd}%o0aDm4%WAr$NJ{w%}BgmUDiKI4PtNv#b@YH5vm!{qRJ`#Zx@b)mS_an(K2(8j$yNWe7KFqe-i* zkb}Q0+quf(e|xvt^5MZOwd*(N0}`q48!@+YcV&(|joaJPv-?>wy3=N@)c~k!^Hz#{ zTJ_=3PIx~z_NxD*d}?1_byA3T8D)gB(wC8Zac4W%M9z&UUgkdIBJ9E|_fxL*^weGc z{Nv2z&c|@$Jbs#%t*jLH2sP@p)E7>Rdwu>?kKXFuqyVUW1-j+cC9PS5_4SA^^bfq{ zqrnnAzVRF4ec`v$P7TGrUm)tN9$=>rc6#pt*+C6meXDJ>ler99BDY0FscAOI1YHQa z=nF+Qafp|OI0lD5Iqz@Q_Lm_z@ALEm-W^7H5UbZc;~+~O++}P*&Q%tg@4PI*7{?{wRAdy`ntOo{?+) zOE;qp30p;!1n#=PGBxbD1!zoe3q4KmO!+m(6icYtPkk!Tc&1P7)tW2BZ6Dmc=QN9V z!CF~G>C@gZ9p>TdoX5un{dIHBfrqTMrZnfO>7;FXBOUKQWA_tF_-=JN&t=~=<W5*M6W%*4uFfnr`krtRS$ z55#G%4Rq*roV?y!EQV@ZRFj}#F7VNaK6ZC>%Dl_bb$Ch}*7#PPyDV1+np5H?d<409 zSU|$CF>HanT$q{5mt7{byW>eb`mf0ATr9rxyf#>xySgQK|3Ze@8#u#_vh8m226`*+ zNXvY~=hy(%_D`fuewO~c!%j^1`xCQFcct&zICVYNqSW@bI|j`;CH|RilMOuU+0e?d)V?aV=$9M^z8}J_ZF$O zgYV0tupt_aZ*H(38Nf>16>rQ$cL(pSRHaDDB5F@dG;CRFGxMjnSauEk7DB;%KTo{9Kr}wT>hY(NEZVk# zDkFX#Z%?Tmr6sw7zP`JYkD`nBsEPBHORe{ho44C8x~Dxq zH}Aqt&CSg&c=m~B8yl6&zP6W_uJ7MnsQK04tp;y)`ZPr1-kIb{(q#_lbmjgKBZ<<< zI?F$T5s5~p5^+h%W=rH29G#LV`-r9{iH@@l8}2%U?vsr&hoX?7B@zO&rXK_c*RtdU z`(iqOJA)>HZxJ-9zSQKM#1b%oxKG@0v3-~ zOjRGkK!_OxF|Zu}zNTt`6v<)2u_p^7Uckms(~vnOnMYJu1aN4Lzv+fThS?}VRTlA# z1lcOKW@{9ixveR&%bdDq5}Eg}P)R9#tw|ZA>I|Gu-$HL^cujP(8|f{2l028XS%SxwQ1g+vv67>PF1G!kJxSv~c_E)L3>?3+??)H+_3YpkH2?O#O`4War z6xB4^7k!Xp`Ggz3}@`|FtaVU*Yqlt1`-J(0Q%WsB@U&ZdgfG0o?8j@C} z-TAcTJ|qyWP^KdAP$DSG+F7{{u)5`W4%q+f{Rs~t!vC8d>Uuh<9+l!(*V51X_l#$i2}GtA;zcvEo*tI(1_j8>M^ zx0unx6T1NB2MkrH`FCnU*$Td>b{ zFZQ{UpiCgbH`sF=XnX8bfyyOR<@$8g=ofmBc717;9Zp`6uF9~iVO$K_LNxcBv2xNT z{_2ue%zX!>XMJYu6sJrE7@8FC;uHL;;BWBoQFx}%C&+Sezfnq{)nt$;a*MhP657J! zgu~#Zf=~NH!pY%Sr6dLP=m`(+oOQjR_{42+R+fAzv%4a@r5Gjw)$o!B!(Ar3DV@&ymgF1pj3PcZMc~QMOn!BS837U^ zxZ6yhBf$zWCdq{-m*GpE4T>aG!t0U-VzQ&yi17Ksc9l@XkTmxsN(y_D(v{MQN|Y(m zGK56;s`gt7G)*ZD2}TQLOPmF?<(xFhLdB5ha@J(pDT}4lQyoy|qhK0yiKe5Tp!pH` z&HG&1MtBrZmyHg*cR=pxoZ`znz|odIQXfJhNvWk%&cfqI06a#<#a zI8}}^B}&NC1peUFWzb5iaAkpsW{moR7${JPvV}S_FhKR}inI(@WQ4|Y)S4neNIMC> z>vjHyB-cjaiO6wSdb;1F88dH99kZDUdZ|TxmEMbR;$6qrs+F82WdjlV4gG`W7otJ7 z%Cc(DCILZ3oK-P~5A|U+pNJNi^G?j7Q&z@95E9V77Lkr7n4n@F!b&mQD2Iwy7;BF@ zLWDX>spn6s%UuVdhZ@H!k;W;F$)X@bxH_XnP*O)QNL0YY;(RoK1po{14xAI@+Z@VZ z(z#bYvDhdB4Ppovs33jAl12G3WN3vxvcgK(hZ;KA2XfKcl;TbJ^u114(%SONkk@%% zXzbJ~glgr~XQ>dPo>vy88W@GrPwD5xTN=Q+m`-Vm3a5{p%}aJSA!NFaR~3oLmFlRP zZH967sXyR!k@Q4w*3gVc8%vZl2#m^c*t)~fGQWoQYJX2`raB^flNFQez~AW*jTE|z zI^+H4f)0k=>2DI1p9*7YI#B^@jv8jA$z$d=88w+1T9{OY>UA9v66oLU zL|oq~Nox&ScR&AD;ozKQR*Ou;$fdNEshlHB3L6rlHIxT5v>fF{cwQ~y@af7hTnMX7 zjJb;CQ~j`YRT8;r2T3w^dj?j33V96D2r5q&`UHTTETo?s^;`+ffOQjxC(xthREeAp znGyr2Vt7Up2|sHizwJ9g3z15aIY=G5gJ|~t`8h2O$%~0Q2%6g2*h?8z!K%&@Iii&O zIsw1e5tCuIFxyW1fJFI;dm@OceYiIHuXrsr!pp*_BVO9|aDE=Iqo?XaWvxanNYjj} zWPnFDN~E;v3B)IQx6l*Qga(aDKlSp0ZYZr~!1 zL>~d~y9bTM{*l?7T_nb+p`U&Atpq0rbUREN_ zT;4ZvG29_X*}KSJI64Bgzh4l&c{|g%?|s-Z@;2P-aOm=iui@ck$bL(T6Hj}E+TE$X zgHu)3Oya`>n%NY+cl&`U4`1re=76hG`Il=b-J~d+64j%LmhioV4<&egjzvRL-D4c$ z3u)yKSh4~gS{fNi2bWjnb*0D~7nLciam(#l;Wk6n9T^>3p3yuqqshw#=SlWVR+#d~ z{JfC^5A$(5(_S)sk$VrxUU)2bOy`^?*M`oxEls*=o=Oc3g~7;#g{KD<=FL$vXEQG! zd&g&C&)BrSJgyyCI`nT37vRzebOe$Z+p-(HYmOJT7N~S~ncJ>fi(ajV-_6Wy z4)v~CUC&H+yS^?TYR7BY6Ya#g>yLorYio9aUh6J&TQ)*!`~FqURi8kwCh+@QF#1X@ zg7_!6D+!ncg6v3|NCmNcw@;p1PLJL^b1UE1gaz&eK#QCI9rpHyDMVESQ#b=G?5(&0AD%AHuAIn%Nf5AGTq=cG{&u1 z`<;XTm0Xijg~AI)X`$Hat#XDpA56cTyY{hs+t|zmn1%-qb80p|HtldH6BC$P*4TQl zRE(Nbyu>hKB2E7s=-xT`;Cl18(o1gurEQpvfj^?hytr-lxj zpe8Kl%$4c8HaotO!-MRq&b+nyw=GA9Mo@j z)6DilZT%EgduUagW;nE(GB*WR*1WzXE73lZl0`M_L{UiDyue7mo@y55c*ETcp1}<9 zMNQX;C0?nOVQ%E1m;#O|uF+OT+L7Us2Bjh&fKq%6?ADaAM&_#L{=y5HG?b8&WumN0y7n=y7?05BCeM zZfu+vQO~~H>;iG@XW{Fp(kbZ&tC_>kZ3%)lo3QxHW7EhNw{KpB?lJoBBzrMtMRYS_ z%jyQbwK8KS91I4A47w`+3N>|bbhG?B&U89s$bM-MHR!r{ zkmr4)6lS%^$`SV&6|ml5uF42sI&w}}kFpD#5s^M|OYIYN!u2PFyfgLz5}TN&XLyxN zL~j|zOI-D1z;3&T?^pP+HqNHTkj()eg>I-dhephvRV;93nAMb{*7D@K)}sxP6t7;# z(vrXp?G9|kV8&(Xet&ckSm~f1><_?v24mD)%F3HUJ7|An;obs^#Y)zTP@jjh#%1az zCH2VGu_7nwrAF5vlt?d$GqyoWPuSq%;*3Or8;6`-dBcmer-`XbH6mr+kuep322`cp zFn&GtHF*3hkbuqsb24W=K@P1~S~8t2d5<3~-!8evZ)6ComAP32&^uDdbEcG91_r~~ zNe_k>bn&d8b2h@t+T4Bvwx7$A)6U3t8Qu^q2R?9qrF4k+5!S(9MZ~za^Rvaf`an9W z0YCI`cDu~=kUQwtTJ90z*FBs8n?9+nkMY4GoesdW(iJM#__O*bU}B z#IC|*0i|4RmpgLuDCNVG(`6pdqdFe%biJ{bFbjbbo{s9BBTE}Ha0AvikH-NVoFnz^ zS`7;Vn3|?Z22-?=oGDwHbA?^Dnpi3iY}A#O?ZvsX7DH%I@-OD7-A~&bA7c(gat~sU zIa3ZpluqC2@@s7~4Y>tzr?h$$bLdR5dJ&4FjzmDzh~iCrKuGnZGOJyDJZN$RZzDER z=@U4{$CrYNC3$;|GMWDg=wTzK=(R7cA03ig>gr@~{+NWzK4Ud&-0@z7^s8QTWOT1M zv2&oP{&amBuzW_FAdq`vRVl;ld$FA3i82MZM+q7}-%My^*1ibfTr-n$lqA!C`}Bm> zr#aLxRE)JBU}+XfmN1!*EX;V!vTpX^PH?8GnT`O=cpa@-)jkk9x(^ynZ%JN=G z6823=^a1VJ4}vMU`BFbLES%`)7p-8sCk#2x@iem&$Esze5N+0%TnnG(z9 zwa&n_+5Z4w@tOy1h(83Gw5Li;@z%u`xLGjDo=;m33V7C~fp$24?4+`{uUKy|$55>v z5*Boe3!1{A3D8H-_d9u{p{~wF9a~@cnc0fk+o#2Clb{T>t*FU5ljyPXLbl_J z1F>~VZ}$V%{bR4wO${eujjEvGa2Z5q&cVgYA+S6nR ztz~GQB~nc%l)=rDi-gwqjM;%N1Vo}2&qjq@Yk~*sZ8vF_x^7AifkI&<^^Y3+!{;h1 zsXHz0n-t!u{eZ-=(jHxiSwR~*b%-`LV(AiA8jo4^GhS8oGmp8s+#!j~U?}c^J0$|~ z)t+cZT`7rV1{~7iBxt}F6`xbAP3+23wJlJUv`v{@NbK|MeA`r~#aq7P2AfgWi(J3` zUZ%_ACMDqJtfsBuS;p86(Y!8}A(7`zy6?r@gb|r+5-r6|F2z<$z$yS4Aur51p{A)( zKfrkZtrfCFl~{%=o+V8>LrlkHN<$7@;+VagIx3lD*StmOh*I!Nx}~IIE9Ms9xZ^$I z12r+Rb|q;}Qx^BB_HgTXW4;ud6f1uHB!b`;J`*g~ zu+mz3%GSaSTbstJi>t)bN0ZGN%3NbN#&JOAxoyq11>{OKFRmHfm>ci7VxR3$HG}GE z?0v-)YUJ&GVaj)4`f<@%IPT`?)D@Sa;lj3`xTnrEe8M^_VlOhhLWVw*VFMv1ud}-h z`T|w5ZM70~e**plmR{E0wfe;^LA9Q~mIo8=^_yN^+DGs8+={aIzJwYyI16d+wyM=H zp$jQ^3~Vz&8)USlT+|K=RQ^iI_)MHh3#hw!&^$<5ivCxPdC*7NvAB7Sq;;tNP&@`F zMIR#tVW>skLpb&LbVUk6Q8T*0D@(lk>njYBabNg;^G3nIs~?5l7oO@z*%b_gb$l-b z$*n*Pj$ef0i=^C7(lK5Ef80anrYDwzUtFR1)F_@lYMxKWD) zp}xrdK{;a;_SF~u$PQGr!7&uIKn2tZ)TeF2E9-qlFN*d0 zyG{DNP#CDd|LWk+A7PLv_`(lo{NfDZ|32Mezeqoyh2(vr(spIucBNZk|GY&-0%O0% z&89JQ@kL~bw6W>)j?&N14P9RCWJI{zw=*FE#$uBPMBfZCoRh2RsQBI!!rrcMY z)o8cN01K5uLu_ne(HgX%5>pDqgb6vpJ@el6z`ERV_@&p@irtLeQbzxe|B6koM)y}>UG zXJfb2b2#;4;%78$q3PuDE}&L`79sm_8WlOUd16$S$(zvn zLBtip?1**)3yhA*bU3q-HDvzloSJ?bGpU;<(gkMDT?&_n0olxpaFl%I*sYb$=otuD4)ixUH1`w!sj8*lrO`P_ z{;Gcdr@oQIxA~yz@CT*#pVj}Tg`aK3s{H7b+CSBvUs?~r*RCcxNge3NOy28E^zhh- z4)HVT#3?_rHFNZD<@@5wbPdOv3_}3Rn@IsLkAWo_UcQw^Un<3@_!&XBbr?C4Gf%Y~ z6ko6YB(ISKTAE_1s}!*fCVge=aeN`$@>y5;dbTT&KyTgXcRYt>Yp(J41 z-gWZIgjTH|uf(15Y3{;%Z%#U|^iUUHoTZ3{@PQJ`A`hh%ZH1K?n!2rGlK_sB!U72q ztC1cwf~7Ro35DbF({_M7{#@+nY)D|UXHu@oMz-liJV)zl0mjSo*e`F==5V>P&lRl< z_uZ5TJ2=9o38t(cE*n1iV)f#@FgW$Ay=}8;uA2UC8hHOksu*97Y&&ywuOQxH$%LvK zYL0nYUZdvb6iH$B48=J7Z8IB3u_=QwmA$DhTw$Vu5^!!`#ebzFolHvQ@v|_dU1ysY zWInat#e3t;yD_s^EfgnK%|&Dk`ur<7mn{dD*{iW>2;%aqro-KGpCf1e={YY`1snfy zwfFO>xIu0Y=}j@^5hEN%$nkkz!eJI2*?Z7N2I|3lo>j2)&w$Jy%?V0PP0Lm-24^xTlsi6yl~HjjAW~vtrqFqHFfjFpar>*Vv9?bUT{G znJLeNd7pX9$6w(z9hzZE%mrBYei6K>qR`|p;QH>W5aw2jd=nd|aD=$uoyWQcdBW$U#9z&(#>-aY4SVivD|l;b;Y5YJ+2Teo3)zmH#sH0)CTYPcjO z^3}_fW8Qts+}nUgv^Gcz4cRhwn81pLi{pZg`?Z@URZBX-6E8GXsHmDZc3``saR783 z%NIDGzx0T5&83coFookyUoG3OImM5zr zB#-v|>)8O`m}X;6d`-dV#-8VxW@mh&Et^H%jvGQD&(`RM)nQqd$ZEf1A0WKdVvVK$ zgKJJ5+1u?3Q<_!u3Vhj1x6|G#=RFQPDFmiEME;uiPh^HG4K9bM86HW#S^-m~1YUQy zROvBtmwsK}bze?rh0TEk^8aOJhX?*4Q68j|bqhL*{?9Da#n?dUH!p4nc-Sd(!1cCS2xDDDvDBFCAOR9EEjt7&cosr z6zaP-Mh6ud*CDgn80Df!qk-pUPN%GKU;2hIU7{10bGirRyY+*@-csj{o$*iZW2qNB zJLl2s{Ty#n2IC(zT=h~kwWgU%F(Umt1`Bu(Q)qIB){B)fjobdxT}U5pk4RefoS>#TRj3s{wFRR5>>>@~c#dyTgmi z(zqGUFF;~C^{fFV1PW(G^qYA*qhyqzZKg3vXbMAmZ2I*=b#BD)17-nut|7ZuSgiTI zi>D~K^K}ufk?>K5xUn3(v^WFsZL6rHP@C#c+N`J0J^*b zRg1;xIgs0TX*@vTGl$BIE)hc4!Qu?c5cyJ9J7Fe%T&aHk20SzN}rvwHXTrBB|f)lR{D%^wH z4{+l+*H;gY4+=5`JW||k_@WE{{R*>1d#?H;xzCS8AgCbWK#*FXg1wEc1B0QB{a+Ut zC>_ZE)B-{GevjA@P!uM**mYpL|G3Vq*#@*bHcfP5nFVYQ85s|;Hct-ABvG;5Jv=dX zwR{VLufmzCb6P`)$zjSpD`ag>bTjD*)%{lSsyvE2j|+ z(d5WYh!*HUG)pCIsSO~ChxMBkQgjk)RiR#>D$S}egOpOFd=H7nYs@E>H4j#CVS~v! zz*0S}|N3K^*$d~3ra64htTFfV4Cr?cEVrKbmqks~69t(Gki!mSHSdL=d3Zj+JhZh5 zmENuGe*Ow5XRsdM-zO==e_b=_3p;ySl{osA=eg`^=Ij)G0P^NHMy2N#-XG^W5_i|^ zan$6#%O8Nlm^OQ0%ch{+Ssr0M>m~IF zN=WtJuVZ!s=W^bGEP4xM(I~%Lw7#wFk0-|e3>sw3pijD@to$Y;O52AX?+|;N3}|0! zRyLt>RXn{0MTl*Sg}5G#{(8&v3GWU=aAGr?w5j2=lpPKRh&da7Dji0Vqr(C%y3%Sv zP^zZ-rFi6#vEF7{1Ar>rCav`#THje})`O3yErIYPHbPFNozfN64NYIpp&g2yq_K3u zY~U+WDdB#tC5uoViY%Dg&>0n{ffKrHl_E?p5?b-T(xi)yyupDo27Y!5c@I2XyjE1W zn-9F^iG%si<^zN_V;SEOM5{r{!ySqS$7DggfZ%L?Px66bwzChVz71B0+&+5ljh`vk zva3xkdT8tRwFWenc3Rllv0peZvgImxZO^l9hw;65r(vbWDR!D--*>2z2ssE{*Fbqp z&V1YGuJuI3^>Rj3U(aWiPX1U|JyDC)*SO(@p08Bvu+O1jg%Lbhx3IqVz^pZD*YEo8 z@o+9Z7Abk32*uWhzh%4DSM`O+E;6frm-GYB- zDLVA5Av8BojE(=2OGJQP)+9A=xhUw&acEs4dVg#^p~34qv3Y_t#>)~{q0_sI-^lJC zi$lZ@O^_tx4uV-acD6^U3D`f65AIskFW zVFGupcap4aGN-O$nkVnpORrVY5>rbTrLf|8I4xU+6pXpXjGrS5G_eG^pzrg*fj|il-r7Pbr@I`TwGD5&B8-yVw6y_*3tT zUvQ$=KjDA)Vm!q^eUtnPAFupR^Z$9T{1p6jf#4VT*5D`j$#TI{hNm;!UkvU>KN@!3fkJOIoG5)5qNhmbVb{{t4A32FcU literal 0 HcmV?d00001 diff --git a/proj5/machinelearning/__pycache__/backend.cpython-313.pyc b/proj5/machinelearning/__pycache__/backend.cpython-313.pyc old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/__pycache__/models.cpython-313.pyc b/proj5/machinelearning/__pycache__/models.cpython-313.pyc old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/autograder.py b/proj5/machinelearning/autograder.py old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/backend.py b/proj5/machinelearning/backend.py old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/chargpt.py b/proj5/machinelearning/chargpt.py old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/data/lang_id.npz b/proj5/machinelearning/data/lang_id.npz old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/data/mnist.npz b/proj5/machinelearning/data/mnist.npz old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/gpt_model.py b/proj5/machinelearning/gpt_model.py old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/input.txt b/proj5/machinelearning/input.txt old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/models.py b/proj5/machinelearning/models.py old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/proj5-readme.txt b/proj5/machinelearning/proj5-readme.txt old mode 100644 new mode 100755 diff --git a/proj5/machinelearning/solution_steps.md b/proj5/machinelearning/solution_steps.md old mode 100644 new mode 100755