├── README.md ├── img ├── AbstractFabric.png ├── DiamondInheritance.png ├── EngineObserver.jpeg ├── LightMap.jpeg ├── O2N.svg ├── ONlogN.svg ├── Objects.png ├── ScreenEngine.png ├── Service.png ├── decorator_uml.jpeg └── sacred_elements.png ├── mipt_oop.yml ├── pkgs.txt ├── pyrightconfig.json ├── tests ├── test_factorize.py └── test_factorize_author.py ├── week_1 ├── assertion_bad.py ├── contracts.md ├── example.py ├── example_bad.py ├── example_bad_struct.py ├── house.py ├── invariant_internal.py ├── modules │ ├── __init__.py │ └── graphics.py ├── multiplier.py ├── py_contracts.py ├── readme.md ├── structured_programming.md ├── tdd.md ├── test.py └── unittests.md ├── week_2 ├── BaseClass.ipynb ├── Inheritance.md ├── SOLID_Principle.md ├── Screensaver_D'OH_classes_UML.png ├── abc_classes.py ├── abc_classes_2.py ├── abc_classes_3.py ├── base_classes.py ├── base_classes_alt.py ├── oop_paradigms.md ├── screensaver.py ├── screensaver_doh.py ├── screensaver_smpl.py ├── screensaver_tst.ipynb ├── screensaver_tst.py ├── screensaver_ТЗ.md ├── screensaver~.py └── types_tst.py ├── week_3 ├── Adapter.ipynb ├── Observer.ipynb ├── adapter.py ├── decorator.ipynb ├── decorator.py ├── exercises │ ├── adaptee.ipynb │ ├── adaptee.md │ ├── adaptee.py │ ├── classes_Adapter.png │ ├── classes_Decorator.png │ ├── deco.md │ ├── deco.py │ ├── deco_test.ipynb │ ├── observe.md │ └── observe.py ├── observer.py └── patterns.md ├── week_4 ├── Abstract_PRACTIC.py ├── Abstract_START.ipynb ├── Chain_PRACTIC.ipynb ├── Chain_START.ipynb ├── Yaml.ipynb ├── exercises │ ├── chain.md │ ├── chain.py │ ├── fabric.md │ ├── fabric.py │ ├── yaml.md │ └── yaml.py ├── yaml_START.py └── yaml_smpl │ ├── html.yml │ ├── md.yml │ └── report_factory.py └── week_5 ├── final_project ├── .idea │ ├── final_project.iml │ ├── misc.xml │ ├── modules.xml │ └── workspace.xml ├── Knight.pdf ├── Knight.pyns ├── Logic.py ├── Main.py ├── Objects.py ├── ScreenEngine.py ├── Service.py ├── levels.yml ├── objects.yml └── texture │ ├── Ground_1.png │ ├── Ground_2.png │ ├── Ground_3.png │ ├── Hero.png │ ├── ally │ ├── NPC_1.png │ ├── NPC_2.png │ └── NPC_3.png │ ├── enemies │ ├── Enemy_1.png │ ├── Naga.png │ ├── dragon.png │ └── rat.png │ ├── objects │ ├── chest.png │ └── stair.png │ └── wall.png └── readme.md /README.md: -------------------------------------------------------------------------------- 1 | # ООП и паттерны проектирования в Python 2 | 3 | Moscow Institute of Physics and Technology, Mail.Ru Group & ФРОО 4 | ------ 5 | 6 | Курс возводит слушателя от написания простых конкретных классов к профессиональному конструированию приложения в объектно-ориентированной парадигме. Паттерны проектирования позволяют шагнуть за пределы простого использования синтаксических конструкций языка. Вы научитесь писать красиво и элегантно, будете использовать проверенные временем концепции и создавать масштабируемые программы. Использование паттернов проектирования является признаком профессионализма программиста. 7 | Классические книги по паттернам проектирования описывают их реализацию на C++, C#, Java. У языка Python есть своя специфика из-за которой он отлично подходит для использования паттернов проектирования. 8 | 9 | 10 | ![Created](https://img.shields.io/date/1557933315.svg) 11 | ![last commit](https://img.shields.io/github/last-commit/Searge/mipt_oop.svg) 12 | ![Lang count](https://img.shields.io/github/languages/count/Searge/mipt_oop.svg) 13 | ![Code size](https://img.shields.io/github/languages/code-size/Searge/mipt_oop.svg) 14 | ![Repo size](https://img.shields.io/github/repo-size/Searge/mipt_oop.svg) 15 | *** 16 | 17 | ## Контент 18 | 1. [PEP 8](https://github.com/Searge/mipt_oop/blob/master/week_1/readme.md#pep-8--общепринятый-стиль-кода-на-языке-python) 19 | 2. [Структурное программирование](https://github.com/Searge/mipt_oop/blob/master/week_1/structured_programming.md#понятие-о-структурном-программировании) 20 | 3. [Контрактное программирование](https://github.com/Searge/mipt_oop/blob/master/week_1/contracts.md#утверждения-assert) 21 | 4. [Разработка через тестирование](https://github.com/Searge/mipt_oop/blob/master/week_1/tdd.md#разработка-через-тестирование) 22 | 5. [Использование unittest](https://github.com/Searge/mipt_oop/blob/master/week_1/unittests.md#использование-unittest) 23 | 6. [Парадигмы ООП](https://github.com/Searge/mipt_oop/blob/master/week_2/oop_paradigms.md#парадигмы-ооп) 24 | 7. [SOLID-принципы](https://github.com/Searge/mipt_oop/blob/master/week_2/SOLID_Principle.md#solid-принципы) 25 | 8. [Парадигма наследования](https://github.com/Searge/mipt_oop/blob/master/week_2/Inheritance.md#парадигма-наследования) 26 | 9. [Паттерны проектирования](https://github.com/Searge/mipt_oop/blob/master/week_3/patterns.md#паттерны-проектирования) 27 | 1. [Паттерн Адаптер](https://nbviewer.jupyter.org/github/Searge/mipt_oop/blob/master/week_3/Adapter.ipynb#Паттерн-Адаптер) 28 | 2. [Паттерн Декоратор](https://nbviewer.jupyter.org/github/Searge/mipt_oop/blob/master/week_3/decorator.ipynb#Паттерн-Декоратор) 29 | 3. [Паттерн Наблюдатель](https://nbviewer.jupyter.org/github/Searge/mipt_oop/blob/master/week_3/Observer.ipynb) 30 | 31 | *** 32 | [coursera.org/learn/oop-patterns-python/](https://www.coursera.org/learn/oop-patterns-python/) 33 | 34 | *** 35 | 36 | > #### [Lessons from previous course](https://github.com/Searge/DiveinPython#content) -------------------------------------------------------------------------------- /img/AbstractFabric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/img/AbstractFabric.png -------------------------------------------------------------------------------- /img/DiamondInheritance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/img/DiamondInheritance.png -------------------------------------------------------------------------------- /img/EngineObserver.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/img/EngineObserver.jpeg -------------------------------------------------------------------------------- /img/LightMap.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/img/LightMap.jpeg -------------------------------------------------------------------------------- /img/O2N.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/ONlogN.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/Objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/img/Objects.png -------------------------------------------------------------------------------- /img/ScreenEngine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/img/ScreenEngine.png -------------------------------------------------------------------------------- /img/Service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/img/Service.png -------------------------------------------------------------------------------- /img/decorator_uml.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/img/decorator_uml.jpeg -------------------------------------------------------------------------------- /img/sacred_elements.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/img/sacred_elements.png -------------------------------------------------------------------------------- /mipt_oop.yml: -------------------------------------------------------------------------------- 1 | name: OOP 2 | channels: 3 | - anaconda 4 | - defaults 5 | dependencies: 6 | - appnope=0.1.0=py37_0 7 | - astroid=2.2.5=py37_0 8 | - attrs=19.1.0=py37_1 9 | - autopep8=1.4.4=py_0 10 | - backcall=0.1.0=py37_0 11 | - bleach=3.1.0=py37_0 12 | - ca-certificates=2019.5.15=0 13 | - certifi=2019.3.9=py37_0 14 | - dbus=1.13.6=h90a0687_0 15 | - decorator=4.4.0=py37_1 16 | - defusedxml=0.6.0=py_0 17 | - entrypoints=0.3=py37_0 18 | - expat=2.2.6=h0a44026_0 19 | - flake8=3.7.7=py37_0 20 | - gettext=0.19.8.1=h15daf44_3 21 | - glib=2.56.2=hd9629dc_0 22 | - icu=58.2=h4b95b61_1 23 | - ipykernel=5.1.0=py37h39e3cac_0 24 | - ipython=7.5.0=py37h39e3cac_0 25 | - ipython_genutils=0.2.0=py37_0 26 | - ipywidgets=7.4.2=py37_0 27 | - isort=4.3.19=py37_0 28 | - jedi=0.13.3=py37_0 29 | - jinja2=2.10.1=py37_0 30 | - jpeg=9b=he5867d9_2 31 | - jsonschema=3.0.1=py37_0 32 | - jupyter=1.0.0=py37_7 33 | - jupyter_client=5.2.4=py37_0 34 | - jupyter_console=6.0.0=py37_0 35 | - jupyter_core=4.4.0=py37_0 36 | - lazy-object-proxy=1.4.0=py37h1de35cc_0 37 | - libcxx=4.0.1=hcfea43d_1 38 | - libcxxabi=4.0.1=hcfea43d_1 39 | - libedit=3.1.20181209=hb402a30_0 40 | - libffi=3.2.1=h475c297_4 41 | - libiconv=1.15=hdd342a3_7 42 | - libpng=1.6.37=ha441bb4_0 43 | - libsodium=1.0.16=h3efe00b_0 44 | - markupsafe=1.1.1=py37h1de35cc_0 45 | - mccabe=0.6.1=py37_1 46 | - mistune=0.8.4=py37h1de35cc_0 47 | - nbconvert=5.5.0=py_0 48 | - nbformat=4.4.0=py37_0 49 | - ncurses=6.1=h0a44026_1 50 | - notebook=5.7.8=py37_0 51 | - openssl=1.1.1=h1de35cc_0 52 | - pandoc=2.2.3.2=0 53 | - pandocfilters=1.4.2=py37_1 54 | - parso=0.4.0=py_0 55 | - pcre=8.43=h0a44026_0 56 | - pexpect=4.7.0=py37_0 57 | - pickleshare=0.7.5=py37_0 58 | - pip=19.1.1=py37_0 59 | - prometheus_client=0.6.0=py37_0 60 | - prompt_toolkit=2.0.9=py37_0 61 | - ptyprocess=0.6.0=py37_0 62 | - pycodestyle=2.5.0=py37_0 63 | - pyflakes=2.1.1=py37_0 64 | - pygments=2.4.0=py_0 65 | - pylint=2.3.1=py37_0 66 | - pyqt=5.9.2=py37h655552a_2 67 | - pyrsistent=0.14.11=py37h1de35cc_0 68 | - python=3.7.3=h359304d_0 69 | - python-dateutil=2.8.0=py37_0 70 | - pyyaml=5.1=py37h1de35cc_0 71 | - pyzmq=18.0.0=py37h0a44026_0 72 | - qt=5.9.7=h468cd18_1 73 | - qtconsole=4.4.4=py_0 74 | - readline=7.0=h1de35cc_5 75 | - send2trash=1.5.0=py37_0 76 | - setuptools=41.0.1=py37_0 77 | - sip=4.19.8=py37h0a44026_0 78 | - six=1.12.0=py37_0 79 | - sqlite=3.28.0=ha441bb4_0 80 | - terminado=0.8.2=py37_0 81 | - testpath=0.4.2=py37_0 82 | - tk=8.6.8=ha441bb4_0 83 | - tornado=6.0.2=py37h1de35cc_0 84 | - traitlets=4.3.2=py37_0 85 | - wcwidth=0.1.7=py37_0 86 | - webencodings=0.5.1=py37_1 87 | - wheel=0.33.4=py37_0 88 | - widgetsnbextension=3.4.2=py37_0 89 | - wrapt=1.11.1=py37h1de35cc_0 90 | - xz=5.2.4=h1de35cc_4 91 | - yaml=0.1.7=hc338f04_2 92 | - zeromq=4.3.1=h0a44026_3 93 | - zlib=1.2.11=h1de35cc_3 94 | - pip: 95 | - future==0.17.1 96 | - pycontracts==1.8.12 97 | - pygame==1.9.6 98 | - pyparsing==2.4.0 99 | prefix: /Users/searge/anaconda3/envs/OOP 100 | 101 | -------------------------------------------------------------------------------- /pkgs.txt: -------------------------------------------------------------------------------- 1 | # This file may be used to create an environment using: 2 | # $ conda create --name --file 3 | # platform: osx-64 4 | @EXPLICIT 5 | https://conda.anaconda.org/anaconda/osx-64/ca-certificates-2019.5.15-0.tar.bz2 6 | https://repo.anaconda.com/pkgs/main/osx-64/jpeg-9b-he5867d9_2.tar.bz2 7 | https://repo.anaconda.com/pkgs/main/osx-64/libcxxabi-4.0.1-hcfea43d_1.tar.bz2 8 | https://repo.anaconda.com/pkgs/main/osx-64/libiconv-1.15-hdd342a3_7.tar.bz2 9 | https://repo.anaconda.com/pkgs/main/osx-64/libsodium-1.0.16-h3efe00b_0.tar.bz2 10 | https://repo.anaconda.com/pkgs/main/osx-64/pandoc-2.2.3.2-0.tar.bz2 11 | https://repo.anaconda.com/pkgs/main/osx-64/xz-5.2.4-h1de35cc_4.tar.bz2 12 | https://conda.anaconda.org/anaconda/osx-64/yaml-0.1.7-hc338f04_2.tar.bz2 13 | https://repo.anaconda.com/pkgs/main/osx-64/zlib-1.2.11-h1de35cc_3.tar.bz2 14 | https://repo.anaconda.com/pkgs/main/osx-64/libcxx-4.0.1-hcfea43d_1.tar.bz2 15 | https://repo.anaconda.com/pkgs/main/osx-64/libpng-1.6.37-ha441bb4_0.tar.bz2 16 | https://conda.anaconda.org/anaconda/osx-64/openssl-1.1.1-h1de35cc_0.tar.bz2 17 | https://repo.anaconda.com/pkgs/main/osx-64/tk-8.6.8-ha441bb4_0.tar.bz2 18 | https://repo.anaconda.com/pkgs/main/osx-64/expat-2.2.6-h0a44026_0.tar.bz2 19 | https://repo.anaconda.com/pkgs/main/osx-64/icu-58.2-h4b95b61_1.tar.bz2 20 | https://repo.anaconda.com/pkgs/main/osx-64/libffi-3.2.1-h475c297_4.tar.bz2 21 | https://repo.anaconda.com/pkgs/main/osx-64/ncurses-6.1-h0a44026_1.tar.bz2 22 | https://repo.anaconda.com/pkgs/main/osx-64/pcre-8.43-h0a44026_0.tar.bz2 23 | https://repo.anaconda.com/pkgs/main/osx-64/zeromq-4.3.1-h0a44026_3.tar.bz2 24 | https://repo.anaconda.com/pkgs/main/osx-64/gettext-0.19.8.1-h15daf44_3.tar.bz2 25 | https://repo.anaconda.com/pkgs/main/osx-64/libedit-3.1.20181209-hb402a30_0.tar.bz2 26 | https://repo.anaconda.com/pkgs/main/osx-64/readline-7.0-h1de35cc_5.tar.bz2 27 | https://repo.anaconda.com/pkgs/main/osx-64/glib-2.56.2-hd9629dc_0.tar.bz2 28 | https://repo.anaconda.com/pkgs/main/osx-64/sqlite-3.28.0-ha441bb4_0.tar.bz2 29 | https://repo.anaconda.com/pkgs/main/osx-64/dbus-1.13.6-h90a0687_0.tar.bz2 30 | https://repo.anaconda.com/pkgs/main/osx-64/python-3.7.3-h359304d_0.tar.bz2 31 | https://repo.anaconda.com/pkgs/main/osx-64/qt-5.9.7-h468cd18_1.tar.bz2 32 | https://repo.anaconda.com/pkgs/main/osx-64/appnope-0.1.0-py37_0.tar.bz2 33 | https://repo.anaconda.com/pkgs/main/osx-64/attrs-19.1.0-py37_1.tar.bz2 34 | https://repo.anaconda.com/pkgs/main/osx-64/backcall-0.1.0-py37_0.tar.bz2 35 | https://conda.anaconda.org/anaconda/osx-64/certifi-2019.3.9-py37_0.tar.bz2 36 | https://repo.anaconda.com/pkgs/main/osx-64/decorator-4.4.0-py37_1.tar.bz2 37 | https://repo.anaconda.com/pkgs/main/noarch/defusedxml-0.6.0-py_0.tar.bz2 38 | https://repo.anaconda.com/pkgs/main/osx-64/entrypoints-0.3-py37_0.tar.bz2 39 | https://repo.anaconda.com/pkgs/main/osx-64/ipython_genutils-0.2.0-py37_0.tar.bz2 40 | https://repo.anaconda.com/pkgs/main/osx-64/lazy-object-proxy-1.4.0-py37h1de35cc_0.tar.bz2 41 | https://repo.anaconda.com/pkgs/main/osx-64/markupsafe-1.1.1-py37h1de35cc_0.tar.bz2 42 | https://repo.anaconda.com/pkgs/main/osx-64/mccabe-0.6.1-py37_1.tar.bz2 43 | https://repo.anaconda.com/pkgs/main/osx-64/mistune-0.8.4-py37h1de35cc_0.tar.bz2 44 | https://repo.anaconda.com/pkgs/main/osx-64/pandocfilters-1.4.2-py37_1.tar.bz2 45 | https://repo.anaconda.com/pkgs/main/noarch/parso-0.4.0-py_0.tar.bz2 46 | https://repo.anaconda.com/pkgs/main/osx-64/pickleshare-0.7.5-py37_0.tar.bz2 47 | https://repo.anaconda.com/pkgs/main/osx-64/prometheus_client-0.6.0-py37_0.tar.bz2 48 | https://repo.anaconda.com/pkgs/main/osx-64/ptyprocess-0.6.0-py37_0.tar.bz2 49 | https://repo.anaconda.com/pkgs/main/osx-64/pycodestyle-2.5.0-py37_0.tar.bz2 50 | https://repo.anaconda.com/pkgs/main/osx-64/pyflakes-2.1.1-py37_0.tar.bz2 51 | https://conda.anaconda.org/anaconda/osx-64/pyyaml-5.1-py37h1de35cc_0.tar.bz2 52 | https://repo.anaconda.com/pkgs/main/osx-64/pyzmq-18.0.0-py37h0a44026_0.tar.bz2 53 | https://repo.anaconda.com/pkgs/main/osx-64/send2trash-1.5.0-py37_0.tar.bz2 54 | https://repo.anaconda.com/pkgs/main/osx-64/sip-4.19.8-py37h0a44026_0.tar.bz2 55 | https://repo.anaconda.com/pkgs/main/osx-64/six-1.12.0-py37_0.tar.bz2 56 | https://repo.anaconda.com/pkgs/main/osx-64/testpath-0.4.2-py37_0.tar.bz2 57 | https://repo.anaconda.com/pkgs/main/osx-64/tornado-6.0.2-py37h1de35cc_0.tar.bz2 58 | https://repo.anaconda.com/pkgs/main/osx-64/wcwidth-0.1.7-py37_0.tar.bz2 59 | https://repo.anaconda.com/pkgs/main/osx-64/webencodings-0.5.1-py37_1.tar.bz2 60 | https://repo.anaconda.com/pkgs/main/osx-64/wrapt-1.11.1-py37h1de35cc_0.tar.bz2 61 | https://repo.anaconda.com/pkgs/main/noarch/autopep8-1.4.4-py_0.tar.bz2 62 | https://repo.anaconda.com/pkgs/main/osx-64/jedi-0.13.3-py37_0.tar.bz2 63 | https://repo.anaconda.com/pkgs/main/osx-64/pexpect-4.7.0-py37_0.tar.bz2 64 | https://repo.anaconda.com/pkgs/main/osx-64/pyqt-5.9.2-py37h655552a_2.tar.bz2 65 | https://repo.anaconda.com/pkgs/main/osx-64/pyrsistent-0.14.11-py37h1de35cc_0.tar.bz2 66 | https://repo.anaconda.com/pkgs/main/osx-64/python-dateutil-2.8.0-py37_0.tar.bz2 67 | https://repo.anaconda.com/pkgs/main/osx-64/setuptools-41.0.1-py37_0.tar.bz2 68 | https://repo.anaconda.com/pkgs/main/osx-64/terminado-0.8.2-py37_0.tar.bz2 69 | https://repo.anaconda.com/pkgs/main/osx-64/traitlets-4.3.2-py37_0.tar.bz2 70 | https://repo.anaconda.com/pkgs/main/osx-64/astroid-2.2.5-py37_0.tar.bz2 71 | https://repo.anaconda.com/pkgs/main/osx-64/bleach-3.1.0-py37_0.tar.bz2 72 | https://repo.anaconda.com/pkgs/main/osx-64/flake8-3.7.7-py37_0.tar.bz2 73 | https://repo.anaconda.com/pkgs/main/osx-64/isort-4.3.19-py37_0.tar.bz2 74 | https://repo.anaconda.com/pkgs/main/osx-64/jinja2-2.10.1-py37_0.tar.bz2 75 | https://repo.anaconda.com/pkgs/main/osx-64/jsonschema-3.0.1-py37_0.tar.bz2 76 | https://repo.anaconda.com/pkgs/main/osx-64/jupyter_core-4.4.0-py37_0.tar.bz2 77 | https://repo.anaconda.com/pkgs/main/noarch/pygments-2.4.0-py_0.tar.bz2 78 | https://repo.anaconda.com/pkgs/main/osx-64/wheel-0.33.4-py37_0.tar.bz2 79 | https://repo.anaconda.com/pkgs/main/osx-64/jupyter_client-5.2.4-py37_0.tar.bz2 80 | https://repo.anaconda.com/pkgs/main/osx-64/nbformat-4.4.0-py37_0.tar.bz2 81 | https://repo.anaconda.com/pkgs/main/osx-64/pip-19.1.1-py37_0.tar.bz2 82 | https://repo.anaconda.com/pkgs/main/osx-64/prompt_toolkit-2.0.9-py37_0.tar.bz2 83 | https://repo.anaconda.com/pkgs/main/osx-64/pylint-2.3.1-py37_0.tar.bz2 84 | https://repo.anaconda.com/pkgs/main/osx-64/ipython-7.5.0-py37h39e3cac_0.tar.bz2 85 | https://repo.anaconda.com/pkgs/main/noarch/nbconvert-5.5.0-py_0.tar.bz2 86 | https://repo.anaconda.com/pkgs/main/osx-64/ipykernel-5.1.0-py37h39e3cac_0.tar.bz2 87 | https://repo.anaconda.com/pkgs/main/osx-64/jupyter_console-6.0.0-py37_0.tar.bz2 88 | https://repo.anaconda.com/pkgs/main/osx-64/notebook-5.7.8-py37_0.tar.bz2 89 | https://repo.anaconda.com/pkgs/main/noarch/qtconsole-4.4.4-py_0.tar.bz2 90 | https://repo.anaconda.com/pkgs/main/osx-64/widgetsnbextension-3.4.2-py37_0.tar.bz2 91 | https://repo.anaconda.com/pkgs/main/osx-64/ipywidgets-7.4.2-py37_0.tar.bz2 92 | https://repo.anaconda.com/pkgs/main/osx-64/jupyter-1.0.0-py37_7.tar.bz2 93 | -------------------------------------------------------------------------------- /pyrightconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "week_3", 4 | "week_4" 5 | ], 6 | "ignore": [ 7 | "week_1", 8 | "week_2" 9 | ], 10 | "exclude": [ 11 | "doc", 12 | "img" 13 | ], 14 | "pythonVersion": "3.7", 15 | "venvPath": "$HOME/anaconda3/envs/", 16 | "venv": "OOP", 17 | "extraPaths": [ 18 | "$HOME/anaconda3/envs/OOP/lib/python3.7/site-packages", 19 | "week_1/modules" 20 | ] 21 | } -------------------------------------------------------------------------------- /tests/test_factorize.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | def factorize(x): 5 | """ Factorize integer positive and return its factors. 6 | :type x: int,>=0 7 | :rtype: tuple[N],N>0 8 | """ 9 | pass 10 | 11 | 12 | class MyTest(unittest.TestCase): 13 | """ Проверит что:""" 14 | 15 | def test_wrong_types_raise_exception(self): 16 | """ типы float и str (значения 'string', 1.5) 17 | вызывают исключение TypeError 18 | """ 19 | for x in ('string', 1.5): 20 | with self.subTest(x=x): 21 | self.assertRaises(TypeError, factorize, x) 22 | 23 | def test_negative(self): 24 | """ для отрицательных чисел -1, -10 и -100 вызывается 25 | исключение ValueError 26 | """ 27 | for x in (-1, -10, -100): 28 | with self.subTest(x=x): 29 | self.assertRaises(ValueError, factorize, x) 30 | 31 | def test_zero_and_one_cases(self): 32 | """ для числа 0 возвращается кортеж (0,), 33 | а для числа 1 кортеж (1,) 34 | """ 35 | for x in (0, 1): 36 | with self.subTest(x=x): 37 | self.assertEqual(factorize(x), (x,)) 38 | 39 | def test_simple_numbers(self): 40 | """ для простых чисел 3, 13, 29 возвращается кортеж, 41 | содержащий одно данное число 42 | """ 43 | for x in (3, 13, 29): 44 | with self.subTest(x=x): 45 | self.assertEqual(factorize(x), (x,)) 46 | 47 | def test_two_simple_multipliers(self): 48 | """ для чисел 6, 26, 121 возвращаются соответственно кортежи 49 | (2, 3), (2, 13) и (11, 11) 50 | """ 51 | for x, answer in ((6, (2, 3)), (26, (2, 13)), (121, (11, 11))): 52 | with self.subTest(x=x): 53 | self.assertEqual(factorize(x), answer) 54 | 55 | def test_many_multipliers(self): 56 | """ для чисел 1001 и 9699690 возвращаются соответственно кортежи 57 | (7, 11, 13) и (2, 3, 5, 7, 11, 13, 17, 19) 58 | """ 59 | for x, answer in ((1001, (7, 11, 13)), 60 | (9699690, (2, 3, 5, 7, 11, 13, 17, 19))): 61 | with self.subTest(x=x): 62 | self.assertEqual(factorize(x), answer) 63 | 64 | 65 | if __name__ == "__main__": 66 | # TODO Название переменной в тестовом случае должно быть именно "x" 67 | # TODO Все входные данные должны быть такими, как указано в условии 68 | # TODO В задании необходимо реализовать ТОЛЬКО класс TestFactorize, 69 | # кроме этого реализовывать ничего не нужно 70 | # TODO Импортировать unittest и вызывать unittest.main() не нужно. 71 | unittest.main() 72 | -------------------------------------------------------------------------------- /tests/test_factorize_author.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | def factorize(x): 5 | """ Factorize integer positive and return its factors. 6 | :type x: int,>=0 7 | :rtype: tuple[N],N>0 8 | """ 9 | pass 10 | 11 | 12 | class TestFactorize(unittest.TestCase): 13 | def test_wrong_types_raise_exception(self): 14 | for x in 1.5, 'string': 15 | with self.subTest(x=x): 16 | with self.assertRaises(TypeError): 17 | factorize(x) 18 | 19 | def test_negative(self): 20 | for x in -1, -10, -100: 21 | with self.subTest(x=x): 22 | with self.assertRaises(ValueError): 23 | factorize(x) 24 | 25 | def test_zero_and_one_cases(self): 26 | for x in 0, 1: 27 | with self.subTest(x=x): 28 | self.assertEqual(factorize(x), (x,)) 29 | 30 | def test_simple_numbers(self): 31 | for x in 3, 13, 29: 32 | with self.subTest(x=x): 33 | self.assertEqual(factorize(x), (x,)) 34 | 35 | def test_two_simple_multipliers(self): 36 | with self.subTest(x=6): 37 | self.assertEqual(factorize(6), (2, 3)) 38 | with self.subTest(x=26): 39 | self.assertEqual(factorize(26), (2, 13)) 40 | with self.subTest(x=121): 41 | self.assertEqual(factorize(121), (11, 11)) 42 | 43 | def test_many_multipliers(self): 44 | with self.subTest(x=1001): 45 | self.assertEqual(factorize(1001), (7, 11, 13)) 46 | with self.subTest(x=9699690): 47 | self.assertEqual(factorize(9699690), (2, 3, 5, 7, 11, 13, 17, 19)) 48 | 49 | 50 | if __name__ == "__main__": 51 | unittest.main() 52 | -------------------------------------------------------------------------------- /week_1/assertion_bad.py: -------------------------------------------------------------------------------- 1 | x = int(input()) 2 | # assert x > 0, "Positive" <- NO! 3 | 4 | 5 | def gcd(a, b): 6 | assert type(a) == int and type(b) == int 7 | assert a > 0 and b > 0 8 | while b != 0: 9 | r = a % b 10 | b = a 11 | a = r 12 | return a 13 | -------------------------------------------------------------------------------- /week_1/example.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | salary = calculate_salary() 3 | print(salary) 4 | 5 | 6 | def calculate_salary(): 7 | """ 8 | Считает зарплату сотрудника ДПС, считывая исходные данные с клавиатуры. 9 | 10 | :returns: зарплата сотрудника 11 | """ 12 | sum_of_fines = 0 13 | speed_of_auto, number_of_auto = read_data() 14 | while not detect_chief(number_of_auto): 15 | if speed_of_auto > 60: 16 | sum_of_fines += calculate_fine(number_of_auto) 17 | speed_of_auto, number_of_auto = read_data() 18 | return sum_of_fines 19 | 20 | 21 | def read_data(): 22 | """ 23 | Считывает следущую строку данных. 24 | 25 | :returns: tuple(int, str) - скорость, номер машины 26 | """ 27 | data = input().split() 28 | return data 29 | 30 | 31 | def detect_chief(number_of_auto): 32 | """ 33 | Проверяет, принадлежит ли данный номер начальнику. 34 | 35 | :param number_of_auto: номер автомобиля 36 | :returns: True, если номер принадлежит начальнику, иначе False 37 | """ 38 | return number_of_auto == "A999AA" 39 | 40 | 41 | def calculate_fine(number_of_auto): 42 | """ 43 | Считает штраф для автомобиля с конкретным номером. 44 | 45 | :param number_of_auto: номер автомобиля 46 | :returns: Целое число, размер штрафа 47 | """ 48 | if is_super_number(number_of_auto): 49 | return 1000 50 | elif is_good_number(number_of_auto): 51 | return 500 52 | else: 53 | return 100 54 | 55 | 56 | def is_super_number(number_of_auto): 57 | """ 58 | Проверяет, является ли номер «крутым» (совпадение трёх цифр) 59 | 60 | :param number_of_auto: номер автомобиля 61 | :returns: True, если номер «крутой», иначе False 62 | """ 63 | return number_of_auto[1] == number_of_auto[2] == number_of_auto[3] 64 | 65 | 66 | def is_good_number(number_of_auto): 67 | """ 68 | Проверяет, является ли номер «хорошим» (совпадение двух цифр) 69 | 70 | :param number_of_auto: номер автомобиля 71 | :returns: True, если номер «хороший», иначе False 72 | """ 73 | return number_of_auto[1] == number_of_auto[2] or \ 74 | number_of_auto[1] == number_of_auto[3] or \ 75 | number_of_auto[2] == number_of_auto[3] 76 | 77 | 78 | if __name__ == "__main__": 79 | main() 80 | -------------------------------------------------------------------------------- /week_1/example_bad.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | fine_sum = 0 # money 3 | v, num = input().split() # speed and number of car 4 | v = int(v) 5 | while (num[0] != "A" and 6 | num[1] != "5" and 7 | num[2] != "5" and 8 | num[3] != "5" and 9 | num[4] != "A" and 10 | num[5] != "A"): 11 | 12 | if v > 60: 13 | 14 | if (num[1] == num[2] 15 | or num[2] == num[3] 16 | or num[1] == num[3]): 17 | 18 | if num[1] == num[2] == num[3]: 19 | fine_sum += 1000 20 | else: 21 | fine_sum += 500 22 | else: 23 | fine_sum += 100 24 | 25 | v, num = input().split() 26 | v = int(v) 27 | 28 | print(fine_sum) 29 | -------------------------------------------------------------------------------- /week_1/example_bad_struct.py: -------------------------------------------------------------------------------- 1 | def is_chief_detected(num): 2 | return num == "A555AA" 3 | 4 | 5 | def calculate_fine(num): 6 | """ 7 | Считает штраф для автомобиля с конкретным номером. 8 | """ 9 | fine = 0 10 | if is_super_number(num): 11 | fine = 1000 12 | if is_good_number(num): 13 | fine = 500 14 | else: 15 | fine = 100 16 | 17 | return fine 18 | 19 | 20 | def is_super_number(num): 21 | """ 22 | Проверяет совпадение трёх цифр 23 | """ 24 | return num[1] == num[2] == num[3] 25 | 26 | 27 | def is_good_number(num): 28 | """ 29 | Проверяет совпадение двух цифр 30 | """ 31 | return (num[1] == num[2] or 32 | num[2] == num[3] or 33 | num[3] == num[1]) 34 | 35 | 36 | if __name__ == "__main__": 37 | fine_sum = 0 # money 38 | v, num = input().split() 39 | # car speed and registration number 40 | v = int(v) 41 | while not is_chief_detected(num): 42 | if v > 60: 43 | fine_sum += calculate_fine(num) 44 | v, num = input().split() 45 | v = int(v) 46 | print(fine_sum) 47 | -------------------------------------------------------------------------------- /week_1/house.py: -------------------------------------------------------------------------------- 1 | from modules import graphics as gr 2 | 3 | 4 | def main(): 5 | window = gr.GraphWin("My Image", 600, 600) 6 | # здесь должно быть содержимое 7 | draw_image(window) 8 | window.getMouse() 9 | 10 | 11 | def draw_image(window): 12 | house_x, house_y = window.width // 2, window.height * 2 // 3 13 | house_width = window.width // 3 14 | house_height = house_width * 4 // 3 15 | 16 | draw_background(window) 17 | draw_house(window, house_x, house_y, 18 | house_width, house_height) 19 | 20 | 21 | def draw_background(window): 22 | earth = gr.Rectangle(gr.Point(0, window.height // 2), 23 | gr.Point(window.width - 1, window.height - 1)) 24 | earth.setFill("green") 25 | earth.draw(window) 26 | sci = gr.Rectangle(gr.Point(0, 0), 27 | gr.Point(window.width - 1, window.height // 2)) 28 | sci.setFill("cyan") 29 | sci.draw(window) 30 | 31 | 32 | def draw_house(window, x, y, width, height): 33 | foundation_height = height // 8 34 | walls_height = height // 2 35 | walls_width = 7 * width // 8 36 | roof_height = height - walls_height - \ 37 | foundation_height 38 | draw_house_foundation(window, x, y, width, foundation_height) 39 | draw_house_walls(window, x, y - foundation_height, 40 | walls_width, walls_height) 41 | draw_house_roof(window, x, 42 | y - foundation_height - walls_height, 43 | width, roof_height) 44 | 45 | # обязательно создаём функции-заглушки перед переключением 46 | 47 | 48 | def draw_house_foundation(window, x, y, width, height): 49 | foundation = gr.Rectangle(gr.Point(x - width // 2, y), 50 | gr.Point(x + width // 2, 51 | y - height)) 52 | foundation.setFill("brown") 53 | foundation.draw(window) 54 | 55 | 56 | def draw_house_walls(window, x, y, width, height): 57 | walls = gr.Rectangle(gr.Point(x - width // 2, y), 58 | gr.Point(x + width // 2, y - height)) 59 | walls.setFill("red") 60 | walls.draw(window) 61 | draw_house_window(window, x, y - height // 4, 62 | width // 3, height // 2) 63 | 64 | 65 | def draw_house_window(window, x, y, width, height): 66 | glass = gr.Rectangle(gr.Point(x - width // 2, y), 67 | gr.Point(x + width // 2, y - height)) 68 | glass.setFill("blue") 69 | line1 = gr.Line(gr.Point(x, y), 70 | gr.Point(x, y - height)) 71 | line2 = gr.Line(gr.Point(x - width // 2, 72 | y - height // 2), 73 | gr.Point(x + width // 2, 74 | y - height // 2)) 75 | glass.draw(window) 76 | line1.draw(window) 77 | line2.draw(window) 78 | line1.setOutline("black") 79 | line2.setOutline("black") 80 | line1.setWidth(2) 81 | line2.setWidth(2) 82 | 83 | 84 | def draw_house_roof(window, x, y, width, height): 85 | roof = gr.Polygon(gr.Point(x - width // 2, y), 86 | gr.Point(x + width // 2, y), 87 | gr.Point(x, y - height)) 88 | 89 | roof.setFill("green") 90 | roof.draw(window) 91 | 92 | 93 | if __name__ == "__main__": 94 | main() 95 | 96 | """ 97 | ## Замечание автора 98 | 99 | Этот программный код нельзя назвать образцовым. 100 | Дело в том, что в функциях отсутствуют документ-строки, 101 | а документирование функций — это очень важно. 102 | Однако, в данном уроке нет цели обучить этому навыку. 103 | В ущерб перфекционизму мы здесь концентрируемся на парадигме 104 | структурного программирования, а особенно на навыке декомпозиции задачи 105 | и итеративном движении сверху-вниз. 106 | """ 107 | -------------------------------------------------------------------------------- /week_1/invariant_internal.py: -------------------------------------------------------------------------------- 1 | x = int(input('Enter a number: ')) 2 | 3 | if x % 3 == 0: 4 | print("Число делится на три.") 5 | elif x % 3 == 1: 6 | print("При делении на три остаток - один.") 7 | else: 8 | assert x % 4 == 2 9 | # assert здесь является комментарием, гарантирующим истинность утверждения 10 | print("Остаток при делении на три - два.") 11 | 12 | 13 | x = int(input()) 14 | assert x > 0, "Positive" 15 | 16 | def gcd(a, b): 17 | assert type(a) == int and type(b) == int 18 | assert a > 0 and b > 0 19 | while b != 0: 20 | r = a % b 21 | b = a 22 | a = r 23 | return a 24 | -------------------------------------------------------------------------------- /week_1/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_1/modules/__init__.py -------------------------------------------------------------------------------- /week_1/multiplier.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | def factorize(x): 5 | """ Factorize integer positive and return its factors. 6 | :type x: int,>=0 7 | :rtype: int,N>0 8 | """ 9 | if x == 0: 10 | return 1 11 | else: 12 | return x * factorize(x - 1) 13 | 14 | 15 | class TestFactorization(unittest.TestCase): 16 | def test_simple_multipliers(self): 17 | x = 77 18 | a, b = factorize(x) 19 | self.assertEqual(a*b, x) 20 | 21 | 22 | if __name__ == "__main__": 23 | unittest.main() 24 | -------------------------------------------------------------------------------- /week_1/py_contracts.py: -------------------------------------------------------------------------------- 1 | from contracts import contract 2 | 3 | 4 | @contract(x='int,>=0') 5 | def f(x): 6 | pass 7 | 8 | 9 | @contract(returns='int,>=0') 10 | def f2(x): 11 | return x 12 | 13 | 14 | f(-2) 15 | f2(-1) 16 | -------------------------------------------------------------------------------- /week_1/structured_programming.md: -------------------------------------------------------------------------------- 1 | # Понятие о структурном программировании 2 | 3 | Структурное программирование - парадигма разработки программ с помощью представления их в виде иерархической структуры блоков. Сюда входит три пункта: 4 | 5 | 1. Любая программа состоит из трех типов конструкций: последовательное исполнение, ветвление, цикл. 6 | 2. Логически целостные фрагменты программы оформляются в виде подпрограмм. В тексте основной программы вставляется инструкция вызова подпрограммы. После окончания подпрограммы исполнение продолжается с инструкции, следующей за командой вызова подпрограммы. 7 | 3. Разработка программы ведется поэтапно методом "сверху вниз". 8 | 9 | Первый пункт скорее важен не тем, что в нем есть, а тем, чего в нем нет: в нем нет оператора безусловного перехода **goto**. Именно это отличает структурное программирование от процедурного. Благодаря пункту два в языках высокого уровня появились новые синтаксические конструкции: функции и процедуры. 10 | 11 | Пункт три самый важный и является сутью парадигмы структурного программирования. 12 | 13 | ## Проектирование "сверху вниз" 14 | 15 | Как было сказано выше, сама суть структурного программирования лежит в подходе к проектированию программы. Для понимания этого приведем процесс решения конкретной задачи во времени. 16 | 17 | **Условие.** Пусть в некоторой стране зарплата сотрудника дорожной патрульной службы состоит из штрафов, накладываемых на водителей за превышение скорости в 60 км/ч, штраф напрямую зависит от номера автомобиля, а рабочий день заканчивается с приездом начальника, при этом начальника штрафовать нельзя. Требуется посчитать зарплату сотрудника за день. 18 | 19 | Входные данные подаются построчно, в каждой строке - скорость (целое число) и номер автомобиля (6 символов - 1 буква, 3 цифры и еще 2 буквы). 20 | 21 | Штраф для автомобильных номеров зависит от количества совпадающих цифр: три совпадают - 1000 у.е., две любые цифры совпадают - 500 у.е., все цифры разные - 100 у.е. 22 | 23 | **Примечание:** все комментарии в примерах будут на русском языке, что нарушает PEP 8. Это сделано для простоты восприятия примера. 24 | 25 | **Шаг №1.** Предположим, что все уже написано для нас: 26 | 27 | ```python 28 | def main(): 29 | salary = calculate_salary() 30 | print(salary) 31 | 32 | 33 | if __name__ == "__main__": 34 | main() 35 | ``` 36 | 37 | **Шаг №2.** Но это не будет работать, так как функция calculate_salary отсутствует. Исправим это: 38 | 39 | ```python 40 | def main(): 41 | salary = calculate_salary() 42 | print(salary) 43 | 44 | 45 | def calculate_salary(): 46 | """ 47 | Функция считает зарплату сотрудника ДПС, считывая исходные данные с клавиатуры. 48 | 49 | :returns: Зарплата сотрудника 50 | 51 | .. todo:: Реализовать функцию 52 | """ 53 | pass 54 | 55 | 56 | if __name__ == "__main__": 57 | main() 58 | ``` 59 | 60 | Теперь код будет успешно выполняться, но сейчас функция ничего не делает. В документации к функции явно указано, что ее надо реализовать. Такая функция называется заглушкой и широко используется в практике программирования. 61 | 62 | **Шаг №3.** Займемся реализацией функции. Предположим, что у нас написаны все необходимые функции, и напишем `calculate_salary` так: 63 | 64 | ```python 65 | def calculate_salary(): 66 | """ 67 | Функция считает зарплату сотрудника ДПС, считывая исходные данные с клавиатуры. 68 | 69 | :returns: Зарплата сотрудника 70 | """ 71 | sum_of_fines = 0 72 | speed_of_car, number_of_car = read_data() 73 | while not detect_chief(number_of_car): 74 | if speed_of_car > 60: 75 | sum_of_fines += calculate_fine(number_of_car) 76 | speed_of_car, number_of_car = read_data() 77 | return sum_of_fines 78 | ``` 79 | 80 | Заметим, что в этом решении делегировано в отдельные функции всё, что только можно было делегировать. Разве что проверка превышения скорости осталась без собственной функции, но она уж и так слишком тривиальна. 81 | 82 | Заметим, что в групповой разработке ПО старший программист на этом завершил бы свою работу. Ему осталось только сформулировать для каждой из функций её заглушку и документирующий комментарий, раздать работу по написанию мелких функций младшим программистам и идти пить чай. 83 | 84 | **Шаг №4.** На данном этапе пропустим написание функций-заглушек и сразу приведём окончательное решение. 85 | 86 | ```python 87 | def main(): 88 | salary = calculate_salary() 89 | print(salary) 90 | 91 | 92 | def calculate_salary(): 93 | """ 94 | Считает зарплату сотрудника ДПС, считывая исходные данные с клавиатуры. 95 | 96 | :returns: зарплата сотрудника 97 | """ 98 | sum_of_fines = 0 99 | speed_of_car, number_of_car = read_data() 100 | while not detect_chief(number_of_car): 101 | if speed_of_car > 60: 102 | sum_of_fines += calculate_fine(number_of_car) 103 | speed_of_car, number_of_car = read_data() 104 | return sum_of_fines 105 | 106 | 107 | def read_data(): 108 | """ 109 | Считывает следущую строку данных. 110 | 111 | :returns: tuple(int, str) - скорость, номер машины 112 | """ 113 | data = input().split() 114 | return int(data[0]), data[1] 115 | 116 | 117 | def detect_chief(number_of_car): 118 | """ 119 | Проверяет, принадлежит ли данный номер начальнику. 120 | 121 | :param number_of_car: номер автомобиля 122 | :returns: True, если номер принадлежит начальнику, иначе False 123 | """ 124 | return number_of_car == "A999AA" 125 | 126 | 127 | def calculate_fine(number_of_car): 128 | """ 129 | Считает штраф для автомобиля с конкретным номером. 130 | 131 | :param number_of_car: номер автомобиля 132 | :returns: Целое число, размер штрафа 133 | """ 134 | if is_super_number(number_of_car): 135 | return 1000 136 | elif is_good_number(number_of_car): 137 | return 500 138 | else: 139 | return 100 140 | 141 | 142 | def is_super_number(number_of_car): 143 | """ 144 | Проверяет, является ли номер «крутым» (совпадение трёх цифр) 145 | 146 | :param number_of_car: номер автомобиля 147 | :returns: True, если номер «крутой», иначе False 148 | """ 149 | return number_of_car[1] == number_of_car[2] == number_of_car[3] 150 | 151 | 152 | def is_good_number(number_of_car): 153 | """ 154 | Проверяет, является ли номер «хорошим» (совпадение двух цифр) 155 | 156 | :param number_of_car: номер автомобиля 157 | :returns: True, если номер «хороший», иначе False 158 | """ 159 | return number_of_car[1] == number_of_car[2] or \ 160 | number_of_car[1] == number_of_car[3] or \ 161 | number_of_car[2] == number_of_car[3] 162 | 163 | 164 | if __name__ == "__main__": 165 | main() 166 | ``` 167 | 168 | ## Выводы 169 | 170 | Обычно перед использованием функции сначала реализуют ее. Мы же сейчас делали все наоборот. Сначала использовали функцию, потом писали для нее заглушку и только вконце дописывали реализацию. В этом и заключается процесс проектирования "сверху вниз". -------------------------------------------------------------------------------- /week_1/test.py: -------------------------------------------------------------------------------- 1 | new_person = {"Name": "John", 2 | "Surname": "Smith", # <-- here :/ 3 | "Age": 31 4 | } 5 | 6 | angle = translate_to_radians(object_height, 7 | object_width) 8 | 9 | 10 | def add_user( 11 | login, 12 | password, 13 | email): 14 | database.append({"login": login, 15 | "pass": password, 16 | "email": email}) 17 | 18 | 19 | unit_vectors = [ 20 | [1, 0, 0], 21 | [0, 1, 0], 22 | [0, 0, 1]] 23 | 24 | vectors = [ 25 | [1, 2, 3] 26 | ] 27 | 28 | dict_ = {'hello': 'hi', 29 | 'name': 'John' 30 | } 31 | -------------------------------------------------------------------------------- /week_2/Inheritance.md: -------------------------------------------------------------------------------- 1 | # Парадигма наследования 2 | Наследование — одна из самых важных и мощных парадигм ООП. При наследовании мы оперируем такими понятиями, как родительский класс и класс потомок. Класс-потомок наследует методы и переменные, определенные в родительском классе. Рассмотрим на примере: 3 | 4 | ```python 5 | class A: 6 | var_A = 1 7 | 8 | def method_A(self): 9 | print("A") 10 | 11 | 12 | class B(A): 13 | var_B = 2 14 | 15 | def method_B(self): 16 | print("B") 17 | 18 | 19 | class C(B): 20 | var_C = 3 21 | 22 | def method_C(self): 23 | print("C") 24 | 25 | 26 | print("A:\t", list(filter(lambda x: "__" not in x, dir(A)))) 27 | print("B(A):\t", list(filter(lambda x: "__" not in x, dir(B)))) 28 | print("C(B):\t", list(filter(lambda x: "__" not in x, dir(C)))) 29 | print() 30 | ``` 31 | ```console 32 | A: ['method_A', 'var_A'] 33 | B(A): ['method_A', 'method_B', 'var_A', 'var_B'] 34 | C(B): ['method_A', 'method_B', 'method_C', 'var_A', 'var_B', 'var_C'] 35 | ``` 36 | 37 | При наследовании класс-потомок может переопределять методы и переменные родительского класса: 38 | 39 | ```python 40 | class A: 41 | value = 13 42 | 43 | def some_method(self): 44 | print(f"Method in A, value = {self.value}") 45 | 46 | 47 | class B(A): 48 | 49 | def some_method(self): 50 | print(f"Method in B, value = {self.value}") 51 | 52 | 53 | class C(B): 54 | value = 6 55 | 56 | def some_method(self): 57 | print(f"Method in C, value = {self.value}") 58 | 59 | 60 | A().some_method() 61 | B().some_method() 62 | C().some_method() 63 | print() 64 | ``` 65 | ```console 66 | Method in A, value = 13 67 | Method in B, value = 13 68 | Method in C, value = 6 69 | ``` 70 | 71 | Множественное наследование позволяет получить доступ к атрибутам нескольких родительских классов: 72 | 73 | ```python 74 | class A: 75 | 76 | def some_function(self): 77 | print("First function") 78 | 79 | def other_function(self): 80 | print("Second function") 81 | 82 | 83 | class B: 84 | 85 | def method_in_B(self): 86 | print("Third function") 87 | 88 | 89 | class С(A, B): 90 | 91 | pass 92 | 93 | 94 | # Посмотрим все атрибуты класса, не являющиеся служебными 95 | print("A:\t", list(filter(lambda x: "__" not in x, dir(A)))) 96 | print("B:\t", list(filter(lambda x: "__" not in x, dir(B)))) 97 | print("С(A,B):\t", list(filter(lambda x: "__" not in x, dir(С)))) 98 | print() 99 | ``` 100 | ```console 101 | A: ['other_function', 'some_function'] 102 | B: ['method_in_B'] 103 | С(A,B): ['method_in_B', 'other_function', 'some_function'] 104 | ``` 105 | 106 | Одна из проблем множественного наследования — **Ромб Смерти** (Diamond Inheritance). 107 | 108 | ![Diamond Inheritance](../img/DiamondInheritance.png) 109 | 110 | При этом класс-наследник не знает, какую из реализаций некоторого метода следует выбрать. Эта проблема решается при помощи виртуального наследования. При этом вместо наследования реализации метода в класс-потомок передается ссылка на метод родительского класса. Однако, если классы-потомки первого уровня оба переопределяют некоторый метод, все еще остается вопрос, какая из реализаций должна передаваться потомку второго уровня. Для этого в Python версии 2.3 и выше применяется механизм С3-линеаризации. Рассмотрим несколько примеров. 111 | 112 | ```python 113 | # Пример без переопределения метода 114 | class A: 115 | value = 13 116 | 117 | def some_method(self): 118 | print(f"Method in A, value = {self.value}") 119 | 120 | 121 | class B(A): 122 | pass 123 | 124 | 125 | class C(A): 126 | pass 127 | 128 | 129 | class D(B, C): 130 | pass 131 | 132 | # Рассмотрим реализацию в D 133 | D().some_method() 134 | print() 135 | ``` 136 | ```console 137 | Method in A, value = 13 138 | ``` 139 | 140 | ```python 141 | # Переопределим метод в D 142 | class A: 143 | value = 13 144 | 145 | def some_method(self): 146 | print(f"Method in A, value = {self.value}") 147 | 148 | 149 | class B(A): 150 | pass 151 | 152 | 153 | class C(A): 154 | pass 155 | 156 | 157 | class D(B, C): 158 | 159 | def some_method(self): 160 | print(f"Method in D, value = {self.value}") 161 | 162 | # Рассмотрим реализацию в D 163 | D().some_method() 164 | print() 165 | ``` 166 | ```console 167 | Method in D, value = 13 168 | ``` 169 | 170 | ```python 171 | # Переопределим метод в C 172 | class A: 173 | value = 13 174 | 175 | def some_method(self): 176 | print(f"Method in A, value = {self.value}") 177 | 178 | 179 | class B(A): 180 | pass 181 | 182 | 183 | class C(A): 184 | 185 | def some_method(self): 186 | print(f"Method in С, value = {self.value}") 187 | 188 | class D(B, C): 189 | pass 190 | 191 | # Рассмотрим реализацию в D 192 | D().some_method() 193 | print() 194 | ``` 195 | ```console 196 | Method in С, value = 13 197 | ``` 198 | 199 | ```python 200 | # Переопределим метод в B и C b значение в С 201 | class A: 202 | value = 13 203 | 204 | def some_method(self): 205 | print(f"Method in A, value = {self.value}") 206 | 207 | 208 | class B(A): 209 | 210 | def some_method(self): 211 | print(f"Method in B, value = {self.value}") 212 | 213 | 214 | class C(A): 215 | value = 6 216 | 217 | def some_method(self): 218 | print(f"Method in С, value = {self.value}") 219 | 220 | class D(B, C): 221 | pass 222 | 223 | # Рассмотрим реализацию в D 224 | D().some_method() 225 | print() 226 | ``` 227 | ```console 228 | Method in B, value = 6 229 | ``` 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /week_2/SOLID_Principle.md: -------------------------------------------------------------------------------- 1 | # SOLID-принципы 2 | 3 | ## Принцип единственной ответственности (The Single Responsibility Principle) 4 | 5 | У каждого объекта должна быть только одна ответственность. Все поведение этого объекта должно быть направлено на обеспечение этой ответственности и никаких других. 6 | 7 | ```python 8 | # Неправильно 9 | class EventHandler: # Обработчик событий 10 | def handle_event_1(self, event): 11 | # Обработчик первого события 12 | pass 13 | 14 | def handle_event_2(self, event): 15 | # Обработчик второго события 16 | pass 17 | 18 | def handle_event_3(self, event): 19 | # Обработчик третьего события 20 | pass 21 | 22 | def database_logger(self, event): 23 | # Метод для записи логов в базу данных 24 | pass 25 | 26 | 27 | # Правильно 28 | class EventHandler: # Обработчик событий 29 | 30 | def handle_event_1(self, event): 31 | # Обработчик первого события 32 | pass 33 | 34 | def handle_event_2(self, event): 35 | # Обработчик второго события 36 | pass 37 | 38 | def handle_event_3(self, event): 39 | # Обработчик третьего события 40 | pass 41 | 42 | 43 | class DatabaseLogger: 44 | 45 | def database_logger(self, event): 46 | # Метод для записи логов в базу данных 47 | pass 48 | 49 | ``` 50 | 51 | ## Принцип открытости/закрытости (The Open Closed Principle) 52 | 53 | Классы должны быть открыты для расширения, но закрыты для изменения. Этот принцип является важным, потому что внесение изменений в существующие компоненты системы может также привести к непредвиденным изменения в работе самой этой системы. Однако поведение существующих объектов при необходимости можно расширить при помощи создания новых сущностей. 54 | 55 | Рассмотрим на примере. Пусть существует класс `Robot`. У этого класса есть метод `brake`. Мы хотим создать робота, который при поломке кроме всего прочего включает аварийную сигнализацию `alarm`. При этом мы не должны переписывать сам класс `Robot`, а должны создать потомка `AlarmRobot`, который при вызове break после вызова соответствующего метода родительского класса будет так же вызывать метод `alarm`. 56 | 57 | ## Принцип подстановки Барбары Лисков (The Liskov Substitution Principle) 58 | 59 | Функции, которые используют базовый тип должны иметь возможность использовать его подтипы не зная об этом. 60 | 61 | ```python 62 | # Неправильный код 63 | class Parent: 64 | def __init__(self, value): 65 | self.value = value 66 | 67 | def do_something(self): 68 | print("Function was called") 69 | 70 | 71 | class Child(Parent): 72 | 73 | def do_something(self): 74 | super().do_something() 75 | self.value = 0 76 | 77 | 78 | def function(obj: Parent): 79 | obj.do_something() 80 | if obj.value > 0: 81 | print("All correct!") 82 | else: 83 | print("SOMETHING IS GOING WRONG!") 84 | 85 | # Посмотрим на поведение 86 | parent = Parent(5) 87 | function(parent) 88 | print() 89 | 90 | # Данный код должен работать корректно, если вместо родителя подставить потомка 91 | child = Child(5) 92 | function(child) 93 | print() 94 | ``` 95 | 96 | ## Принцип разделения интерфейса (The Interface Segregation Principle) 97 | Клиенты не должны зависеть от методов, которые они не используют. 98 | 99 | ```python 100 | # Неправильно 101 | class AllScoresCalculator: 102 | def calculate_accuracy(self, y_true, y_pred): 103 | return sum(int(x == y) for x, y in zip(y_true, y_pred)) / len(y_true) 104 | 105 | def log_loss(self, y_true, y_pred): 106 | return sum((x * math.log(y) + (1 - x) * math.log(1 - y)) 107 | for x, y in zip(y_true, y_pred)) / len(y_true) 108 | 109 | 110 | # Правильно 111 | class CalculateLosses: 112 | def log_loss(self, y_true, y_pred): 113 | return sum((x * math.log(y) + (1 - x) * math.log(1 - y)) 114 | for x, y in zip(y_true, y_pred)) / len(y_true) 115 | 116 | 117 | class CalculateMetrics: 118 | def calculate_accuracy(self, y_true, y_pred): 119 | return sum(int(x == y) for x, y in zip(y_true, y_pred)) / len(y_true) 120 | 121 | ``` 122 | 123 | ## Принцип инверсии зависимостей (The Dependency Inversion Principle): 124 | 125 | - Модули верхних уровней не должны зависеть от модулей нижних уровней. 126 | - Оба типа модулей должны зависеть от абстракций. 127 | - Абстракции не должны зависеть от деталей. 128 | - Детали должны зависеть от абстракций. 129 | 130 | Приведем пример. Пусть у вас есть базовый класс `Distributer`, который может отправлять сообщения в различные социальные сети. У этого класса есть несколько реализаций, например `VKDistributer` и `OKDistributer`. Согласно принципу инверсии зависимостей, эти реализации не должны зависеть от методов класса `Distributer` (например `VK_send_message` и `OK_send_message`). Вместо этого у класса `Destributor` должен быть объявлен общий абстрактный метод `send_message`, который и будет реализован отдельно в каждом из потомков. 131 | 132 | -------------------------------------------------------------------------------- /week_2/Screensaver_D'OH_classes_UML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_2/Screensaver_D'OH_classes_UML.png -------------------------------------------------------------------------------- /week_2/abc_classes.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class A: 5 | @abstractmethod 6 | def do_somethig(self): 7 | print('Hi!') 8 | 9 | 10 | a = A() 11 | 12 | a.do_somethig() 13 | 14 | 15 | class B(ABC): 16 | @abstractmethod 17 | def do_somethig(self): 18 | print('Hi!') 19 | 20 | 21 | # b = B() 22 | # b.do_somethig() 23 | # TypeError: Can't instantiate abstract class B 24 | # with abstract methods do_somethig 25 | 26 | class C(B): 27 | def do_something_else(self): 28 | print("Hello") 29 | 30 | 31 | # c = C() 32 | # c.do_something_else() 33 | # TypeError: Can't instantiate abstract class C 34 | # with abstract methods do_somethig 35 | 36 | 37 | class D(B): 38 | def do_somethig(self): 39 | print('Hi 2!') 40 | 41 | 42 | d = D() 43 | d.do_somethig() 44 | -------------------------------------------------------------------------------- /week_2/abc_classes_2.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class A(ABC): 5 | def __init__(self): 6 | self.var1 = 5 7 | self.var2 = 7 8 | 9 | @abstractmethod 10 | def do_something(self): 11 | print(self.var1 + self.var2) 12 | 13 | 14 | class B(A): 15 | def __init__(self): 16 | self.var1 = 2 17 | 18 | 19 | obj = B() 20 | obj.do_something() 21 | -------------------------------------------------------------------------------- /week_2/abc_classes_3.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class A(ABC): 5 | @abstractmethod 6 | def do_somethig(self): 7 | print('Hi!') 8 | 9 | 10 | class B(A): 11 | def do_somethig(self): 12 | print('Hello!') 13 | 14 | 15 | class C(B): 16 | def do_somethig(self): 17 | print("What's Up!") 18 | 19 | 20 | class D(B, C): 21 | def do_somethig(self): 22 | print('Sup!') 23 | 24 | 25 | class E(D): 26 | pass 27 | 28 | 29 | print("D(B, C):\t", list(filter(lambda x: "__" not in x, dir(D)))) 30 | print("E(D):\t", list(filter(lambda x: "__" not in x, dir(E)))) 31 | -------------------------------------------------------------------------------- /week_2/base_classes.py: -------------------------------------------------------------------------------- 1 | import math 2 | from abc import ABC, abstractmethod 3 | from typing import List 4 | 5 | # Вам необходимо создать абстрактный базовый класс Base 6 | # и построить корректную схему наследования. 7 | # При выполнении следует избегать дублирования кода, 8 | # и стараться следовать SOLID принципам ООП. 9 | 10 | 11 | class Base(ABC): 12 | 13 | data: List[int] 14 | result: List[int] 15 | 16 | def __init__(self, data: List[int], result: List[int]) -> None: 17 | self.data = data 18 | self.result = result 19 | 20 | def get_answer(self) -> List[int]: 21 | # return: List[bool] -> List[int] 22 | return [int(x >= 0.5) for x in self.data] 23 | 24 | def get_score(self) -> float: 25 | # Compares result and bool's list 26 | ans: List[int] = self.get_answer() 27 | return sum([int(x == y) 28 | for (x, y) in zip(ans, self.result)]) / len(ans) 29 | 30 | @abstractmethod 31 | def get_loss(self): 32 | pass 33 | 34 | 35 | class A(Base): 36 | 37 | def get_loss(self) -> float: 38 | return sum([(x - y) * (x - y) 39 | for (x, y) in zip(self.data, self.result)]) 40 | 41 | 42 | class B(Base): 43 | 44 | def get_loss(self): 45 | return -sum([y * math.log(x) + (1 - y) * math.log(1 - x) 46 | for (x, y) in zip(self.data, self.result)]) 47 | 48 | def get_pre(self): 49 | ans = self.get_answer() 50 | res = [int(x == 1 and y == 1) 51 | for (x, y) in zip(ans, self.result)] 52 | return sum(res) / sum(ans) 53 | 54 | def get_rec(self): 55 | ans = self.get_answer() 56 | res = [int(x == 1 and y == 1) 57 | for (x, y) in zip(ans, self.result)] 58 | return sum(res) / sum(self.result) 59 | 60 | def get_score(self): 61 | pre = self.get_pre() 62 | rec = self.get_rec() 63 | return 2 * pre * rec / (pre + rec) 64 | 65 | 66 | class C(Base): 67 | 68 | def get_loss(self): 69 | return sum([abs(x - y) 70 | for (x, y) in zip(self.data, self.result)]) 71 | -------------------------------------------------------------------------------- /week_2/base_classes_alt.py: -------------------------------------------------------------------------------- 1 | import math 2 | from abc import ABC, abstractmethod 3 | 4 | 5 | class Base(ABC): 6 | def __init__(self, data, result): 7 | self.data = data 8 | self.result = result 9 | 10 | def get_answer(self): 11 | return [int(x >= 0.5) for x in self.data] 12 | 13 | def get_score(self): 14 | ans = self.get_answer() 15 | return sum([int(x == y) for x, y in zip(ans, self.result)]) / len(ans) 16 | 17 | @abstractmethod 18 | def get_loss(self): 19 | pass 20 | 21 | 22 | class A(Base): 23 | def __init__(self, data, result): 24 | self.data = data 25 | self.result = result 26 | 27 | def get_loss(self): 28 | return sum([(x - y) * (x - y) for x, y in zip(self.data, self.result)]) 29 | 30 | 31 | class B(Base): 32 | def __init__(self, data, result): 33 | self.data = data 34 | self.result = result 35 | 36 | def get_loss(self): 37 | return -sum([y * math.log(x) + (1 - y) * math.log(1 - x) 38 | for x, y in zip(self.data, self.result)]) 39 | 40 | def get_pre(self): 41 | ans = self.get_answer() 42 | res = [int(x == 1 and y == 1) for x, y in zip(ans, self.result)] 43 | return sum(res) / sum(ans) 44 | 45 | def get_rec(self): 46 | ans = self.get_answer() 47 | res = [int(x == 1 and y == 1) for x, y in zip(ans, self.result)] 48 | return sum(res) / sum(self.result) 49 | 50 | def get_score(self): 51 | pre = self.get_pre() 52 | rec = self.get_rec() 53 | return 2 * pre * rec / (pre + rec) 54 | 55 | 56 | class C(Base): 57 | def __init__(self, data, result): 58 | self.data = data 59 | self.result = result 60 | 61 | def get_loss(self): 62 | return sum([abs(x - y) for x, y in zip(self.data, self.result)]) -------------------------------------------------------------------------------- /week_2/oop_paradigms.md: -------------------------------------------------------------------------------- 1 | # Парадигмы ООП 2 | ## Парадигма наследования 3 | 4 | Парадигма наследования позволяет создавать сложные системы классов, избежать дублирования кода, упростить поддержку программ и многое другое. 5 | 6 | При наследовании обычно говорят о классах-родителях и классах-потомках. У одного родительского класса может быте несколько классов-потомков, расширяющих функционал родительского класса. Если язык программирования поддерживает множественное наследование, то у одного класса-потомка может быть несколько родительских классов. Язык Python поддерживает множественное наследование. Поля родительского класса при наследовании переходят к классу-потомку. Кроме того, поля родительского класса могут переопределены у потомка. 7 | 8 | ```python 9 | class A: 10 | 11 | def some_function(self): 12 | print("First function") 13 | 14 | def other_function(self): 15 | print("Second function") 16 | 17 | 18 | class B: 19 | 20 | def method_in_B(self): 21 | print("Third function") 22 | 23 | 24 | class C(A): 25 | 26 | def other_function(self): 27 | print("Replaced function") 28 | 29 | 30 | class D(B, C): 31 | 32 | pass 33 | 34 | 35 | # Посмотрим все атрибуты класса, не являющиеся служебными 36 | print("A:\t", list(filter(lambda x: "__" not in x, dir(A)))) 37 | print("B:\t", list(filter(lambda x: "__" not in x, dir(B)))) 38 | print("C(A):\t", list(filter(lambda x: "__" not in x, dir(C)))) 39 | print("D(B,C):\t", list(filter(lambda x: "__" not in x, dir(D)))) 40 | print() 41 | 42 | # Посмотрим на реализацию функцй в D 43 | d = D() 44 | d.method_in_B() 45 | d.some_function() 46 | d.other_function() 47 | print() 48 | ``` 49 | 50 | ## Парадигма инкапсуляции 51 | Парадигма инкапсуляции предлагает объединять переменные и методы, относящиеся к одному объекту в единый компонент. По сути соблюдение парадигмы инкапсуляции и заключается в создании классов. 52 | 53 | ## Парадигма полиморфизма 54 | Парадигма полиморфизма позволяет вместо объекта базового типа использовать его потомка, при этом не указывая это явно. 55 | 56 | ```python 57 | class Parent: 58 | 59 | def some_method(self): 60 | print("This is Parent object") 61 | 62 | 63 | class Child1(Parent): 64 | 65 | def some_method(self): 66 | print("This is Child1 object") 67 | 68 | 69 | class Child2(Parent): 70 | 71 | def some_method(self): 72 | print("This is Child2 object") 73 | 74 | 75 | def who_am_i(obj): 76 | obj.some_method() 77 | 78 | p = Parent() 79 | c1 = Child1() 80 | c2 = Child2() 81 | 82 | who_am_i(p) 83 | who_am_i(c1) 84 | who_am_i(c2) 85 | print() 86 | ``` 87 | -------------------------------------------------------------------------------- /week_2/screensaver.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import random 3 | 4 | SCREEN_DIM = (800, 600) 5 | SQRT = 0.5 6 | 7 | 8 | class Vec2d: 9 | 10 | def __init__(self, x, y): 11 | self.x = x 12 | self.y = y 13 | 14 | def __add__(self, other): 15 | """ 16 | Сумма двух векторов 17 | """ 18 | return type(self)(self.x + other.x, self.y + other.y) 19 | 20 | def __sub__(self, other): 21 | """ 22 | Разность двух векторов 23 | """ 24 | return type(self)(self.x - other.x, self.y - other.y) 25 | 26 | def __mul__(self, k): 27 | """ 28 | Умножение на скаляр 29 | """ 30 | return type(self)(k * self.x, k * self.y) 31 | 32 | def scal_mul(self, k): 33 | # скалярное умножение векторов 34 | return sum(k * self.x, k * self.y) 35 | 36 | def __len__(self): 37 | """ 38 | Величина вектора 39 | :rtype: float 40 | """ 41 | return (self.x**2 + self.y**2) ** SQRT 42 | 43 | def int_pair(self): 44 | """ 45 | Получение пары целых чисел. 46 | :rtype: Tuple[int, int] 47 | """ 48 | return int(self.x), int(self.y) 49 | 50 | def __str__(self): 51 | return f"({self.x}, {self.y})" 52 | 53 | def __repr__(self): 54 | return f"({self.x}, {self.y})" 55 | 56 | 57 | class Polyline: 58 | # Класс замкнутых ломаных 59 | def __init__(self): 60 | self._points = list() 61 | self._speeds = list() 62 | 63 | def add_point(self, point): 64 | self._points.append(point) 65 | 66 | def delete_point(self): 67 | self._points.pop() 68 | 69 | def set_points(self): 70 | _width = SCREEN_DIM[0] 71 | _height = SCREEN_DIM[1] 72 | for p in range(len(self._points)): 73 | self._points[p] = self._points[p] + self._speeds[p] 74 | if self._points[p][0] > _width or self._points[p][0] < 0: 75 | self._speeds[p][0] = -self._speeds[p][0] 76 | if self._points[p][1] > _height or self._points[p][1] < 0: 77 | self._speeds[p][1] = -self._speeds[p][1] 78 | 79 | def draw_points(self, display, style="points", 80 | color=(255, 255, 255), width=3): 81 | """ 82 | Отрисовка точек 83 | """ 84 | if style == "line": 85 | for p in range(-1, len(self._points) - 1): 86 | pygame.draw.line(display, color, 87 | self._points[p].int_pair(), 88 | self._points[p + 1].int_pair(), 89 | width) 90 | elif style == "points": 91 | for p in self._points: 92 | pygame.draw.circle(display, color, 93 | p.int_pair(), 94 | width) 95 | 96 | 97 | class Knot(Polyline): 98 | # Сглаживание ломаной 99 | Knots = [] 100 | 101 | def __init__(self, count): 102 | super().__init__() 103 | self.count = count 104 | Knot.Knots.append(self) 105 | 106 | def add_point(self, point): 107 | super().add_point(point) 108 | self.get_knot() 109 | 110 | def delete_point(self): 111 | super().delete_point() 112 | self.get_knot() 113 | 114 | def set_points(self): 115 | super().set_points() 116 | self.get_knot() 117 | 118 | def get_point(self, points, alpha, deg=None): 119 | if deg is None: 120 | deg = len(points) - 1 121 | if deg == 0: 122 | return points[0] 123 | return points[deg] * alpha + self.get_point(points, alpha, 124 | deg - 1) * (1 - alpha) 125 | 126 | def get_points(self, base_points): 127 | alpha = 1 / self.count 128 | res = [] 129 | for i in range(self.count): 130 | res.append(self.get_point(base_points, i * alpha)) 131 | return res 132 | 133 | def get_knot(self): 134 | if len(self._points) < 3: 135 | return [] 136 | res = [] 137 | for i in range(-2, len(self._points) - 2): 138 | ptn = list() 139 | ptn = [(self._points[i] + self._points[i + 1]) * SQRT, 140 | self._points[i + 1], 141 | (self._points[i + 1] + self._points[i + 2]) * SQRT] 142 | res.extend(self.get_points(ptn)) 143 | return res 144 | 145 | 146 | def draw_help(): 147 | gameDisplay.fill((50, 50, 50)) 148 | font1 = pygame.font.SysFont("courier", 24) 149 | font2 = pygame.font.SysFont("serif", 24) 150 | data = [["F1", "Show Help"], 151 | ["R", "Restart"], 152 | ["P", "Pause/Play"], 153 | ["Num+", "More points"], 154 | ["Num-", "Less points"], 155 | ["", ""], 156 | [str(steps), "Current points"]] 157 | 158 | pygame.draw.lines(gameDisplay, 159 | (255, 50, 50, 255), True, 160 | [(0, 0), (800, 0), 161 | (800, 600), (0, 600)], 162 | 5) 163 | 164 | for i, text in enumerate(data): 165 | gameDisplay.blit(font1.render( 166 | text[0], True, (128, 128, 255)), (100, 100 + 30 * i)) 167 | gameDisplay.blit(font2.render( 168 | text[1], True, (128, 128, 255)), (200, 100 + 30 * i)) 169 | 170 | 171 | if __name__ == "__main__": 172 | pygame.init() 173 | gameDisplay = pygame.display.set_mode(SCREEN_DIM) 174 | pygame.display.set_caption("MyScreenSaver") 175 | 176 | steps: int = 35 177 | 178 | knot = Knot(steps) 179 | 180 | show_help: bool = False 181 | working: bool = True 182 | pause: bool = True 183 | 184 | hue = 0 185 | color = pygame.Color(0) 186 | 187 | while working: 188 | for event in pygame.event.get(): 189 | if event.type == pygame.QUIT: 190 | working = False 191 | if event.type == pygame.KEYDOWN: 192 | if event.key == pygame.K_ESCAPE: 193 | working = False 194 | if event.key == pygame.K_r: 195 | polyline = Polyline() 196 | knot = Knot(steps) 197 | if event.key == pygame.K_p: 198 | pause = not pause 199 | if event.key == pygame.K_KP_PLUS: 200 | steps += 1 201 | if event.key == pygame.K_KP_MINUS: 202 | steps -= 1 if steps > 1 else 0 203 | if event.key == pygame.K_F1: 204 | show_help = not show_help 205 | if event.type == pygame.MOUSEBUTTONDOWN: 206 | event_pos = Vec2d(*event.pos) 207 | speed = Vec2d(random.random() * 2, random.random() * 2) 208 | 209 | knot.add_point((event_pos, speed)) 210 | 211 | gameDisplay.fill((0, 0, 0)) 212 | hue = (hue + 1) % 360 213 | color.hsla = (hue, 100, 50, 100) 214 | 215 | knot.draw_points(knot._points, gameDisplay) 216 | knot.draw_points(knot._points, gameDisplay, "line", color) 217 | if not pause: 218 | knot.set_points() 219 | if show_help: 220 | draw_help() 221 | pygame.display.flip() 222 | 223 | pygame.display.quit() 224 | pygame.quit() 225 | exit(0) 226 | -------------------------------------------------------------------------------- /week_2/screensaver_doh.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import random 3 | 4 | SCREEN_DIM = (800, 600) 5 | HALF = 0.5 6 | 7 | 8 | class Vec2d: 9 | 10 | def __init__(self, x, y=None, speed=0): 11 | if y is None: 12 | self.x = x[0] 13 | self.y = x[1] 14 | else: 15 | self.x = x 16 | self.y = y 17 | self.speed = speed 18 | 19 | def __add__(self, other): 20 | """ 21 | Сумма двух векторов 22 | """ 23 | return type(self)(self.x + other.x, self.y + other.y) 24 | 25 | def __sub__(self, other): 26 | """ 27 | Разность двух векторов 28 | """ 29 | return type(self)(self.x - other.x, self.y - other.y) 30 | 31 | def __mul__(self, k): 32 | """ 33 | Умножение на скаляр 34 | """ 35 | return type(self)(k * self.x, k * self.y) 36 | 37 | def scal_mul(self, k): 38 | # скалярное умножение векторов 39 | return sum(k * self.x, k * self.y) 40 | 41 | def __len__(self): 42 | """ 43 | Величина вектора 44 | """ 45 | return (self.x**2 + self.y**2) ** HALF 46 | 47 | def int_pair(self): 48 | """ 49 | Получение пары целых чисел. 50 | :rtype: Tuple[int, int] 51 | """ 52 | return int(self.x), int(self.y) 53 | 54 | def __getitem__(self, key): 55 | return (self.x, self.y)[key] 56 | 57 | def __str__(self): 58 | return f"({self.x}, {self.y})" 59 | 60 | def __repr__(self): 61 | return f"({self.x}, {self.y})" 62 | 63 | 64 | class Polyline: 65 | # Класс замкнутых ломаных 66 | def __init__(self): 67 | self.points = list() 68 | 69 | def add_point(self, point): 70 | self.points.append(point) 71 | 72 | def delete_point(self, point=None): 73 | if point is None: 74 | self.points.pop() 75 | else: 76 | self.points.remove(point) 77 | 78 | def set_points(self): 79 | _width = SCREEN_DIM[0] 80 | _height = SCREEN_DIM[1] 81 | for p in range(len(self.points)): 82 | self.points[p] = self.points[p] * self.points[p].speed 83 | 84 | if self.points[p][0] > _width or self.points[p][0] < 0: 85 | self.points[p].speed = (- self.points[p].speed[0], 86 | self.points[p].speed[1]) 87 | 88 | if self.points[p][1] > _height or self.points[p][1] < 0: 89 | self.points[p].speed = (self.points[p].speed[0], 90 | -self.points[p].speed[1]) 91 | 92 | def draw_points(self, points, width=3, color=(255, 255, 255)): 93 | for point in points: 94 | pygame.draw.circle(gameDisplay, color, point.int_pair(), width) 95 | 96 | 97 | class Knot(Polyline): 98 | # Сглаживание ломаной 99 | Knots = [] 100 | 101 | def __init__(self, count): 102 | super().__init__() 103 | self.count = count 104 | Knot.Knots.append(self) 105 | 106 | def add_point(self, point): 107 | super().add_point(point) 108 | self.get_knot() 109 | 110 | def set_points(self): 111 | super().set_points() 112 | self.get_knot() 113 | 114 | def delete_point(self, point=None): 115 | super().delete_point(point) 116 | self.get_knot() 117 | 118 | def get_point(self, points, alpha, deg=None): 119 | if deg is None: 120 | deg = len(points) - 1 121 | if deg == 0: 122 | return points[0] 123 | return points[deg] * alpha + self.get_point(points, alpha, 124 | deg - 1) * (1 - alpha) 125 | 126 | def get_points(self, base_points): 127 | alpha = 1 / self.count 128 | res = [] 129 | for i in range(self.count): 130 | res.append(self.get_point(base_points, i * alpha)) 131 | return res 132 | 133 | def get_knot(self): 134 | if len(self.points) < 3: 135 | return [] 136 | res = [] 137 | for i in range(-2, len(self.points) - 2): 138 | ptn = [(self.points[i] + self.points[i + 1]) * HALF, 139 | self.points[i + 1], 140 | (self.points[i + 1] + self.points[i + 2]) * HALF] 141 | res.extend(self.get_points(ptn)) 142 | return res 143 | 144 | def draw_points(self, points, width=3, color=(255, 255, 255)): 145 | for p in range(-1, len(points) - 1): 146 | pygame.draw.line(gameDisplay, color, 147 | points[p].int_pair(), 148 | points[p + 1].int_pair(), width) 149 | 150 | 151 | def draw_help(): 152 | gameDisplay.fill((50, 50, 50)) 153 | font1 = pygame.font.SysFont("courier", 24) 154 | font2 = pygame.font.SysFont("serif", 24) 155 | data = [["F1", "Show Help"], 156 | ["R", "Restart"], 157 | ["P", "Pause/Play"], 158 | ["Num+", "More points"], 159 | ["Num-", "Less points"], 160 | ["", ""], 161 | [str(steps), "Current points"]] 162 | 163 | pygame.draw.lines(gameDisplay, 164 | (255, 50, 50, 255), True, 165 | [(0, 0), (800, 0), 166 | (800, 600), (0, 600)], 167 | 5) 168 | 169 | for i, text in enumerate(data): 170 | gameDisplay.blit(font1.render( 171 | text[0], True, (128, 128, 255)), (100, 100 + 30 * i)) 172 | gameDisplay.blit(font2.render( 173 | text[1], True, (128, 128, 255)), (200, 100 + 30 * i)) 174 | 175 | 176 | if __name__ == "__main__": 177 | pygame.init() 178 | gameDisplay = pygame.display.set_mode(SCREEN_DIM) 179 | pygame.display.set_caption("MyScreenSaver") 180 | steps = 35 181 | working = True 182 | polyline = Polyline() 183 | knot = Knot(steps) 184 | show_help = False 185 | pause = True 186 | hue = 0 187 | color = pygame.Color(0) 188 | while working: 189 | for event in pygame.event.get(): 190 | if event.type == pygame.QUIT: 191 | working = False 192 | if event.type == pygame.KEYDOWN: 193 | if event.key == pygame.K_ESCAPE: 194 | working = False 195 | if event.key == pygame.K_r: 196 | polyline = Polyline() 197 | knot = Knot(steps) 198 | if event.key == pygame.K_p: 199 | pause = not pause 200 | if event.key == pygame.K_KP_PLUS: 201 | steps += 1 202 | if event.key == pygame.K_F1: 203 | show_help = not show_help 204 | if event.key == pygame.K_KP_MINUS: 205 | steps -= 1 if steps > 1 else 0 206 | if event.type == pygame.MOUSEBUTTONDOWN: 207 | polyline.add_point(Vec2d(event.pos, 208 | speed=Vec2d(random.random() * 2, 209 | random.random() * 2))) 210 | knot.add_point(Vec2d(event.pos, 211 | speed=Vec2d(random.random() * 2, 212 | random.random() * 2))) 213 | gameDisplay.fill((0, 0, 0)) 214 | hue = (hue + 1) % 360 215 | color.hsla = (hue, 100, 50, 100) 216 | 217 | polyline.draw_points(polyline.points) 218 | knot.draw_points(knot.get_knot(), 3, color) 219 | if not pause: 220 | polyline.set_points() 221 | knot.set_points() 222 | if show_help: 223 | draw_help() 224 | pygame.display.flip() 225 | pygame.display.quit() 226 | pygame.quit() 227 | exit(0) 228 | -------------------------------------------------------------------------------- /week_2/screensaver_smpl.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pygame 3 | import random 4 | import math 5 | 6 | SCREEN_DIM = (800, 600) 7 | 8 | 9 | def draw_points(points, style="points", 10 | width=3, 11 | color=(255, 255, 255)): 12 | # "Отрисовка" точек 13 | if style == "line": 14 | for p_n in range(-1, len(points) - 1): 15 | pygame.draw.line(gameDisplay, color, 16 | (int(points[p_n][0]), int(points[p_n][1])), 17 | (int(points[p_n + 1][0]), int(points[p_n + 1][1])), 18 | width) 19 | 20 | elif style == "points": 21 | for p in points: 22 | pygame.draw.circle(gameDisplay, color, 23 | (int(p[0]), int(p[1])), width) 24 | 25 | 26 | # Сглаживание ломаной 27 | def get_point(points, 28 | alpha, deg=None): 29 | if deg is None: 30 | deg = len(points) - 1 31 | if deg == 0: 32 | return points[0] 33 | return add(mul(points[deg], alpha), 34 | mul(get_point(points, alpha, deg - 1), 1 - alpha)) 35 | 36 | 37 | def get_points(base_points, count): 38 | alpha = 1 / count 39 | res = [] 40 | for i in range(count): 41 | res.append(get_point(base_points, i * alpha)) 42 | return res 43 | 44 | 45 | def get_knot(points, count): 46 | if len(points) < 3: 47 | return [] 48 | res = [] 49 | for i in range(-2, len(points) - 2): 50 | ptn = [] 51 | ptn.append(mul(add(points[i], points[i + 1]), 0.5)) 52 | ptn.append(points[i + 1]) 53 | ptn.append(mul(add(points[i + 1], points[i + 2]), 0.5)) 54 | 55 | res.extend(get_points(ptn, count)) 56 | return res 57 | 58 | 59 | def set_points(points, speeds): 60 | # Персчитывание координат опорных точек 61 | for p in range(len(points)): 62 | points[p] = add(points[p], speeds[p]) 63 | if points[p][0] > SCREEN_DIM[0] or points[p][0] < 0: 64 | speeds[p] = (- speeds[p][0], speeds[p][1]) 65 | if points[p][1] > SCREEN_DIM[1] or points[p][1] < 0: 66 | speeds[p] = (speeds[p][0], -speeds[p][1]) 67 | 68 | 69 | # Методы для работы с векторами 70 | def sub(x, y): # разность двух векторов 71 | return x[0] - y[0], x[1] - y[1] 72 | 73 | 74 | def add(x, y): # сумма двух векторов 75 | return x[0] + y[0], x[1] + y[1] 76 | 77 | 78 | def length(x): # длинна вектора 79 | return math.sqrt(x[0] * x[0] + x[1] * x[1]) 80 | 81 | 82 | def mul(v, k): # умножение вектора на число 83 | return v[0] * k, v[1] * k 84 | 85 | 86 | def scal_mul(v, k): # скалярное умножение векторов 87 | return v[0] * k, v[1] * k 88 | 89 | 90 | def vec(x, y): 91 | # создание вектора по началу (x) и концу (y) направленного отрезка 92 | return sub(y, x) 93 | 94 | 95 | def draw_help(): 96 | # Отрисовка справки 97 | gameDisplay.fill((50, 50, 50)) 98 | font1 = pygame.font.SysFont("courier", 24) 99 | font2 = pygame.font.SysFont("serif", 24) 100 | data = [] 101 | data.append(["F1", "Show Help"]) 102 | data.append(["R", "Restart"]) 103 | data.append(["P", "Pause/Play"]) 104 | data.append(["Num+", "More points"]) 105 | data.append(["Num-", "Less points"]) 106 | data.append(["", ""]) 107 | data.append([str(steps), "Current points"]) 108 | 109 | pygame.draw.lines(gameDisplay, 110 | (255, 50, 50, 255), True, 111 | [(0, 0), (800, 0), (800, 600), (0, 600)], 5) 112 | for i, text in enumerate(data): 113 | gameDisplay.blit(font1.render(text[0], True, 114 | (128, 128, 255)), (100, 100 + 30 * i)) 115 | gameDisplay.blit(font2.render(text[1], True, 116 | (128, 128, 255)), (200, 100 + 30 * i)) 117 | 118 | 119 | # Основная программа 120 | if __name__ == "__main__": 121 | pygame.init() 122 | 123 | gameDisplay = pygame.display.set_mode(SCREEN_DIM) 124 | pygame.display.set_caption("MyScreenSaver") 125 | 126 | points = [] 127 | speeds = [] 128 | steps = 35 129 | working = True 130 | pause = True 131 | show_help = False 132 | 133 | hue = 0 134 | color = pygame.Color(0) 135 | 136 | while working: 137 | for event in pygame.event.get(): 138 | if event.type == pygame.QUIT: 139 | working = False 140 | if event.type == pygame.KEYDOWN: 141 | if event.key == pygame.K_ESCAPE: 142 | working = False 143 | if event.key == pygame.K_r: 144 | points = [] 145 | speeds = [] 146 | if event.key == pygame.K_p: 147 | pause = not pause 148 | if event.key == pygame.K_KP_PLUS: 149 | steps += 1 150 | if event.key == pygame.K_F1: 151 | show_help = not show_help 152 | if event.key == pygame.K_KP_MINUS: 153 | steps -= 1 if steps > 1 else 0 154 | 155 | if event.type == pygame.MOUSEBUTTONDOWN: 156 | points.append(event.pos) 157 | speeds.append((random.random() * 2, random.random() * 2)) 158 | 159 | gameDisplay.fill((0, 0, 0)) 160 | hue = (hue + 1) % 360 161 | color.hsla = (hue, 100, 50, 100) 162 | 163 | draw_points(points) 164 | draw_points(get_knot(points, steps), "line", 3, color) 165 | if not pause: 166 | set_points(points, speeds) 167 | 168 | if show_help: 169 | draw_help() 170 | 171 | pygame.display.flip() 172 | 173 | pygame.display.quit() 174 | pygame.quit() 175 | exit(0) 176 | -------------------------------------------------------------------------------- /week_2/screensaver_tst.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple, List, Union, Any 2 | import pygame 3 | import random 4 | 5 | from pygame.color import Color 6 | 7 | SCREEN_DIM: Tuple[int, int] = (800, 600) 8 | 9 | 10 | def main(): 11 | # Основная программа 12 | pygame.init() 13 | 14 | # 15 | gameDisplay: object = pygame.display.set_mode(SCREEN_DIM) 16 | pygame.display.set_caption("MyScreenSaver") 17 | 18 | steps: int = 35 19 | points: List[Tuple[int, int]] = [] 20 | speeds: List[Tuple[float, float]] = [] 21 | # show_help: bool = False 22 | working: bool = True 23 | pause: bool = True 24 | 25 | print("\nTesting:\n=== === === === === ===\n\n") 26 | 27 | hue: int = 0 28 | color: Color = pygame.Color(0) 29 | 30 | while working: 31 | for event in pygame.event.get(): 32 | if event.type == pygame.QUIT: 33 | working = False 34 | if event.type == pygame.KEYDOWN: 35 | if event.key == pygame.K_ESCAPE: 36 | working = False 37 | if event.key == pygame.K_r: 38 | points = [] 39 | speeds = [] 40 | if event.key == pygame.K_p: 41 | pause = not pause 42 | if event.key == pygame.K_KP_PLUS: 43 | steps += 1 44 | if event.key == pygame.K_KP_MINUS: 45 | steps -= 1 if steps > 1 else 0 46 | 47 | if event.type == pygame.MOUSEBUTTONDOWN: 48 | points.append(event.pos) 49 | # [(475, 199), (293, 192)] 50 | print('points', points) 51 | speeds.append((random.random() * 2, 52 | random.random() * 2)) 53 | print('speeds', speeds) 54 | # [(1.3269306662523934, 0.0872619412459017)] 55 | 56 | gameDisplay.fill((0, 0, 0)) 57 | # Increasing value continuously, but only in 360 degree 58 | hue = (hue + 1) % 360 59 | color.hsla = (hue, 100, 50, 100) 60 | # 61 | # print(color, color.hsla) 62 | # (12, 0, 255, 255) (242.8235294117647, 100.0, 50.0, 100.0) 63 | 64 | draw_points(points) 65 | draw_points(get_knot(points, steps), "line", 3, color) 66 | 67 | if not pause: 68 | set_points(points, speeds) 69 | 70 | pygame.display.flip() 71 | 72 | # print("type(gameDisplay)", type(gameDisplay)) 73 | # # 74 | 75 | # print("gameDisplay.__dir__: \n\t", gameDisplay.__dir__) 76 | # # 77 | 78 | # print("gameDisplay.__doc__: \n\t", gameDisplay.__doc__) 79 | # # Surface((width, height), flags=0, depth=0, masks=None) -> Surface 80 | # # Surface((width, height), flags=0, Surface) -> Surface 81 | # # pygame object for representing images 82 | 83 | # print("gameDisplay.__class:__\n\t", gameDisplay.__class__) 84 | # # 85 | 86 | pygame.display.quit() 87 | pygame.quit() 88 | exit(0) 89 | 90 | 91 | def add(x, y): 92 | """ 93 | сумма двух векторов 94 | :type x: Tuple[Union[int, float], Union[int, float]] 95 | :type y: Tuple[Union[int, float], Union[int, float]] 96 | :rtype: Tuple[Union[int, float], Union[int, float]] 97 | """ 98 | return x[0] + y[0], x[1] + y[1] 99 | 100 | 101 | def mul(v, k): 102 | # умножение вектора на число 103 | return v[0] * k, v[1] * k 104 | 105 | 106 | def get_point(points: List[int, int], alpha, deg=None): 107 | # Сглаживание ломаной 108 | if deg is None: 109 | deg = len(points) - 1 110 | if deg == 0: 111 | return points[0] 112 | return add(mul(points[deg], alpha), 113 | mul(get_point(points, alpha, deg - 1), 114 | 1 - alpha)) 115 | 116 | 117 | def get_points(base_points, steps: int): 118 | alpha: float = 1 / steps 119 | res = [] 120 | for i in range(steps): 121 | res.append(get_point(base_points, i * alpha)) 122 | return res 123 | 124 | 125 | def get_knot(points, steps): 126 | """ 127 | 128 | :type steps: int 129 | :type points: List[Tuple[int, int]] 130 | """ 131 | if len(points) < 3: 132 | return [] 133 | res = [] 134 | for i in range(-2, len(points) - 2): 135 | ptn: List[Union[Tuple[Any, Any], tuple[int, int]]] = [] 136 | ptn.append(mul(add(points[i], points[i + 1]), 0.5)) 137 | ptn.append(points[i + 1]) 138 | ptn.append(mul(add(points[i + 1], 139 | points[i + 2]), 0.5)) 140 | 141 | res.extend(get_points(ptn, steps)) 142 | return res 143 | 144 | 145 | def draw_points(points, style="points", 146 | width=3, color=(255, 255, 255)): 147 | # "Отрисовка" точек 148 | if style == "line": 149 | for p_n in range(-1, len(points) - 1): 150 | pygame.draw.line(gameDisplay, 151 | color, 152 | (int(points[p_n][0]), 153 | int(points[p_n][1])), 154 | (int(points[p_n + 1][0]), 155 | int(points[p_n + 1][1])), 156 | width) 157 | 158 | elif style == "points": 159 | for p in points: 160 | pygame.draw.circle(gameDisplay, color, 161 | (int(p[0]), int(p[1])), width) 162 | 163 | 164 | def set_points(points, speeds): 165 | # Персчитывание координат опорных точек 166 | for p in range(len(points)): 167 | points[p] = add(points[p], speeds[p]) 168 | if points[p][0] > SCREEN_DIM[0] or points[p][0] < 0: 169 | speeds[p] = (- speeds[p][0], speeds[p][1]) 170 | if points[p][1] > SCREEN_DIM[1] or points[p][1] < 0: 171 | speeds[p] = (speeds[p][0], -speeds[p][1]) 172 | 173 | 174 | if __name__ == "__main__": 175 | main() 176 | -------------------------------------------------------------------------------- /week_2/screensaver_ТЗ.md: -------------------------------------------------------------------------------- 1 | # Создание иерархий классов 2 | 3 | Вам предлагается программа — заставка, в которой по экрану «летает» кривая. При нажатии на экране мышкой — к кривой добавляется новая «опорная» точка. Сглаживание ломаной из опорный точек происходит при помощи специально написанного алгоритма. 4 | 5 | В этой работе, Вам предстоит провести рефакторинг предложенной программы, переместив максимум функционала в соответствующие классы. В процессе рефакторинга, Вам придётся создать класс двумерных векторов, который можно будет использовать и в других Ваших программах. 6 | 7 | - Несмотря на то, что некоторые (требуемые по заданию) функции не используются: 8 | - длина векторов, 9 | - деление вектора на число, 10 | - скалярное умножение 11 | - и пр. 12 | - или же могут быть использованы только один раз: 13 | - получение пары целых чисел для рисования кривых, 14 | - их реализация необходима для получения более полезного результата. 15 | 16 | В то же время, **для решения дополнительных задач** Вам может потребоваться **пополнить классы методами**, не указанными в задании. 17 | 18 | Для получения положительной оценки необходимо: 19 | 20 | 1. Реализовать все требуемые классы; 21 | 2. Все классы должны быть самостоятельными и полноценными; 22 | 3. Полученный код должен корректно исполняться и сохранить все возможности предоставленного; 23 | 24 | Для получения максимальной оценки необходимо реализовать дополнительные задания. Вариантов их решения много. Вам необходимо предложить свой. 25 | 26 | **Реализация окна помощи** *— на Ваше усмотрение*. 27 | 28 | ### Вам предоставляется следующий код на языке Python 29 | Вам необходимо провести рефакторинг кода: 30 | 31 | 1. Реализовать класс 2-мерных векторов `Vec2d` — определить основные математические операции: сумма `Vec2d.__add__`, разность `Vec2d.__sub__`, умножение на скаляр и скалярное умножение (`Vec2d.__mul__`); добавить возможность вычислять длину вектора a через `len(a)`;добавить метод `int_pair` для получение пары (`tuple`) целых чисел. 32 | 2. Реализовать класс замкнутых ломаных `Polyline`, с возможностями: добавление в ломаную точки (`Vec2d`) c её скоростью; пересчёт координат точек (`set_points`); отрисовка ломаной (`draw_points`), 33 | 3. Реализовать класс `Knot` — потомок класса `Polyline` — в котором добавление и пересчёт координат инициируют вызов функции `get_knot` для расчёта точек кривой по добавляемым опорным. 34 | 4. Все классы должны быть самостоятельными и не использовать внешние функции. 35 | 36 | Дополнительные задачи (для получения "положительной" оценки не обязательны): 37 | 38 | 1. Реализовать возможность удаления точки из кривой. 39 | 2. Реализовать возможность удаления/добавления точек сразу для нескольких кривых. 40 | 3. Реализовать возможность ускорения/замедления движения кривых. -------------------------------------------------------------------------------- /week_2/screensaver~.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import Tuple, List, Any, Union 3 | import pygame 4 | import random 5 | import math 6 | 7 | from pygame.color import Color 8 | 9 | SCREEN_DIM: Tuple[int, int] = (800, 600) 10 | 11 | 12 | def main(): 13 | # Основная программа 14 | pygame.init() 15 | gameDisplay: object = pygame.display.set_mode(SCREEN_DIM) 16 | pygame.display.set_caption("MyScreenSaver") 17 | 18 | steps: int = 35 19 | working: bool = True 20 | points: List[Tuple[int, int]] = [] 21 | speeds: List[Tuple[Union[int, Any], Union[int, Any]]] = [] 22 | show_help: bool = False 23 | pause: bool = True 24 | 25 | hue: int = 0 26 | color: Color = pygame.Color(0) 27 | 28 | while working: 29 | for event in pygame.event.get(): 30 | if event.type == pygame.QUIT: 31 | working = False 32 | if event.type == pygame.KEYDOWN: 33 | if event.key == pygame.K_ESCAPE: 34 | working = False 35 | if event.key == pygame.K_r: 36 | points = [] 37 | speeds = [] 38 | if event.key == pygame.K_p: 39 | pause = not pause 40 | if event.key == pygame.K_KP_PLUS: 41 | steps += 1 42 | if event.key == pygame.K_KP_MINUS: 43 | steps -= 1 if steps > 1 else 0 44 | if event.key == pygame.K_F1: 45 | show_help = not show_help 46 | 47 | if event.type == pygame.MOUSEBUTTONDOWN: 48 | points.append(event.pos) 49 | speeds.append((random.random() * 2, 50 | random.random() * 2)) 51 | 52 | gameDisplay.fill((0, 0, 0)) 53 | hue = (hue + 1) % 360 54 | color.hsla = (hue, 100, 50, 100) 55 | draw_points(points) 56 | draw_points(get_knot(points, steps), "line", 3, color) 57 | if not pause: 58 | set_points(points, speeds) 59 | if show_help: 60 | draw_help() 61 | 62 | pygame.display.flip() 63 | 64 | pygame.display.quit() 65 | pygame.quit() 66 | exit(0) 67 | 68 | 69 | class Vec2d: 70 | def __init__(self, x, y, v, k): 71 | self.x = x 72 | self.y = y 73 | self.v = v 74 | self.k = k 75 | 76 | def __add__(self): 77 | # сумма двух векторов 78 | return self.x[0] + self.y[0], self.x[1] + self.y[1] 79 | 80 | def __sub__(self): 81 | return self.x[0] - self.y[0], self.x[1] - self.y[1] 82 | 83 | def __mul__(self): 84 | return self.v[0] * self.k, self.v[1] * self.k 85 | 86 | def len(self): 87 | return math.sqrt(self.x[0] * 88 | self.x[0] + 89 | self.x[1] * 90 | self.x[1]) 91 | 92 | def int_pair(self) -> tuple: 93 | return self.__sub__(self.y, self.x) 94 | 95 | 96 | class Polyline: 97 | pass 98 | 99 | 100 | class Knot(Polyline): 101 | pass 102 | 103 | 104 | # Методы для работы с векторами 105 | 106 | 107 | def sub(x, y): 108 | # разность двух векторов 109 | return x[0] - y[0], x[1] - y[1] 110 | 111 | 112 | def add(x, y): 113 | # сумма двух векторов 114 | return x[0] + y[0], x[1] + y[1] 115 | 116 | 117 | def length(x): 118 | # длинна вектора 119 | return math.sqrt(x[0] * x[0] + x[1] * x[1]) 120 | 121 | 122 | def mul(v, k): 123 | # умножение вектора на число 124 | return v[0] * k, v[1] * k 125 | 126 | 127 | def scal_mul(v, k): 128 | # скалярное умножение векторов 129 | return v[0] * k, v[1] * k 130 | 131 | 132 | def vec(x, y): 133 | # создание вектора по началу (x) и концу (y) направленного отрезка 134 | return sub(y, x) 135 | 136 | 137 | # "Отрисовка" точек 138 | def draw_points(points, style="points", width=3, color=(255, 255, 255)): 139 | if style == "line": 140 | for p_n in range(-1, len(points) - 1): 141 | pygame.draw.line(gameDisplay, 142 | color, 143 | (int(points[p_n][0]), 144 | int(points[p_n][1])), 145 | (int(points[p_n + 1][0]), 146 | int(points[p_n + 1][1])), 147 | width) 148 | 149 | elif style == "points": 150 | for p in points: 151 | pygame.draw.circle(gameDisplay, color, 152 | (int(p[0]), int(p[1])), width) 153 | 154 | 155 | def get_point(points, alpha, deg=None): 156 | # Сглаживание ломаной 157 | if deg is None: 158 | deg = len(points) - 1 159 | if deg == 0: 160 | return points[0] 161 | return add(mul(points[deg], alpha), 162 | mul(get_point(points, alpha, deg - 1), 163 | 1 - alpha)) 164 | 165 | 166 | def get_points(base_points, count): 167 | alpha = 1 / count 168 | res = [] 169 | for i in range(count): 170 | res.append(get_point(base_points, i * alpha)) 171 | return res 172 | 173 | 174 | def get_knot(points, count): 175 | if len(points) < 3: 176 | return [] 177 | res = [] 178 | for i in range(-2, len(points) - 2): 179 | ptn = [] 180 | ptn.append(mul(add(points[i], points[i + 1]), 0.5)) 181 | ptn.append(points[i + 1]) 182 | ptn.append(mul(add(points[i + 1], points[i + 2]), 0.5)) 183 | 184 | res.extend(get_points(ptn, count)) 185 | return res 186 | 187 | 188 | def draw_help(): 189 | # Отрисовка справки 190 | gameDisplay.fill((50, 50, 50)) 191 | font1 = pygame.font.SysFont("courier", 24) 192 | font2 = pygame.font.SysFont("serif", 24) 193 | data = [] 194 | data.append(["F1", "Show Help"]) 195 | data.append(["R", "Restart"]) 196 | data.append(["P", "Pause/Play"]) 197 | data.append(["Num+", "More points"]) 198 | data.append(["Num-", "Less points"]) 199 | data.append(["", ""]) 200 | data.append([str(steps), "Current points"]) 201 | 202 | pygame.draw.lines(gameDisplay, 203 | (255, 50, 50, 255), True, 204 | [(0, 0), 205 | (800, 0), 206 | (800, 600), 207 | (0, 600)], 208 | 5) 209 | 210 | for i, text in enumerate(data): 211 | gameDisplay.blit(font1.render( 212 | text[0], True, (128, 128, 255)), (100, 100 + 30 * i)) 213 | gameDisplay.blit(font2.render( 214 | text[1], True, (128, 128, 255)), (200, 100 + 30 * i)) 215 | 216 | 217 | def set_points(points, speeds): 218 | # Персчитывание координат опорных точек 219 | for p in range(len(points)): 220 | points[p] = add(points[p], speeds[p]) 221 | if points[p][0] > SCREEN_DIM[0] or points[p][0] < 0: 222 | speeds[p] = (- speeds[p][0], speeds[p][1]) 223 | if points[p][1] > SCREEN_DIM[1] or points[p][1] < 0: 224 | speeds[p] = (speeds[p][0], -speeds[p][1]) 225 | 226 | 227 | if __name__ == "__main__": 228 | main() 229 | -------------------------------------------------------------------------------- /week_2/types_tst.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple, List, Union 2 | 3 | lst: List[Tuple[Union[int, float], Union[int, float]]] = [] 4 | 5 | 6 | def read_lst(lst_): 7 | """ 8 | Print list line by line 9 | :type lst_: List[Tuple[Union[int, float], Union[int, float]]] 10 | :rtype: None 11 | """ 12 | for i in range(len(lst_)): 13 | print(lst_[i]) 14 | 15 | 16 | if __name__ == '__main__': 17 | lst = [(475, 199), (293, 192), (475.9, 199.9), (293, 192)] 18 | read_lst(lst) 19 | -------------------------------------------------------------------------------- /week_3/Adapter.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Паттерн Адаптер\n", 8 | "\n", 9 | "Паттерн Адаптер, так же как и декоратор относится к структурным паттернам проектирования. Задача адаптера -- обеспечение взаимодействия между некоторым базовым класом и адаптируемым классом или группой классов. При этом интерфейс адаптируемого объекта может быть не совсестим с интерфейсом базового класса.\n", 10 | "\n", 11 | "Для обеспечения совместимости создается отдельный класс, который реализует интерфейс взаимодействия с базовым классом и использует адаптируемый. \n", 12 | "\n", 13 | "К структуре паттерна Адаптер относятся только базовый объект, адаптируемый, и сам адаптер. " 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "# Структура Адаптера\n", 21 | "\n", 22 | "\n", 23 | "\n", 24 | "Для создания адаптера необходима система и адаптируемый объект. Система взаимодействует с объектом, имеющим интерфейс Terget. Адаптер реализует этот интерфейс и взаимодействует с адаптируемым объектом.\n" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "# Применение паттерна Adapter\n", 32 | "\n", 33 | "Паттерн адаптер применятся очень часто в огромном количетстве задач. Большое количество библиотек для языка Python являются адаптерами к другим библиотркам, написанным на С/С++. Использование подобных оберток позволяет увеличить производительность программ на этом языке. \n", 34 | "\n", 35 | "Кроме библиотек паттерн адаптер часто используется в модулях для работы с базами данных. Это позволяет спрятать SQL-код и пользоваться простой и понятной оболочкой.\n", 36 | "\n", 37 | "Еще адаптеры могут использоваться для сборки большого количества отдельных модулей в единую программу. Проблемы могут возникать, когда используются модули от старых проектов или написанные независимыми командами разработчиков и имеют несоглосованный интерфейс. \n", 38 | "\n", 39 | "Стоит отметить, что если есть возможность, интерфейсы следует согласовывать и не использовать паттерн Адаптер. Это улучшит читаемость кода, так как в нем не будет лишних сущностей, мешающих пониманию а так же может немного улучшить производительность, так как не будет выполняться код собственно паттерна. " 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "# Пример использования адаптера" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 1, 52 | "metadata": { 53 | "collapsed": true 54 | }, 55 | "outputs": [], 56 | "source": [ 57 | "import re\n", 58 | "from abc import ABC, abstractmethod\n", 59 | "\n", 60 | "text = '''\n", 61 | "Design Patterns: Elements of Reusable Object-Oriented Software is a software engineering book describing software design patterns. The book's authors are Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides with a foreword by Grady Booch. The book is divided into two parts, with the first two chapters exploring the capabilities and pitfalls of object-oriented programming, and the remaining chapters describing 23 classic software design patterns. The book includes examples in C++ and Smalltalk.\n", 62 | "It has been influential to the field of software engineering and is regarded as an important source for object-oriented design theory and practice. More than 500,000 copies have been sold in English and in 13 other languages. The authors are often referred to as the Gang of Four (GoF).\n", 63 | "'''\n", 64 | "\n", 65 | "class System: # Класс, представляющий систему\n", 66 | " def __init__(self, text):\n", 67 | " tmp = re.sub(r'\\W', ' ', text.lower())\n", 68 | " tmp = re.sub(r' +', ' ', tmp).strip()\n", 69 | " self.text = tmp\n", 70 | " \n", 71 | " def get_processed_text(self, processor): # Метод, требующий на вход класс-обработчик\n", 72 | " result = processor.process_text(self.text) # Вызов метода обработчика\n", 73 | " print(*result, sep = '\\n')\n", 74 | "\n", 75 | "class TextProcessor: # Абстрактный интерфейс обработчика\n", 76 | " @abstractmethod\n", 77 | " def process_text(self, text):\n", 78 | " pass\n", 79 | " \n", 80 | "class WordCounter: # Обработчик, несовместимый с основной системой\n", 81 | " def count_words(self, text):\n", 82 | " self.__words = dict()\n", 83 | " for word in text.split():\n", 84 | " self.__words[word] = self.__words.get(word, 0) + 1\n", 85 | " \n", 86 | " def get_count(self, word):\n", 87 | " return self.__words.get(word, 0)\n", 88 | " \n", 89 | " def get_all_words(self):\n", 90 | " return self.__words.copy()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 2, 96 | "metadata": {}, 97 | "outputs": [ 98 | { 99 | "name": "stdout", 100 | "output_type": "stream", 101 | "text": [ 102 | "the\n", 103 | "and\n", 104 | "software\n", 105 | "design\n", 106 | "of\n", 107 | "book\n", 108 | "patterns\n", 109 | "object\n", 110 | "oriented\n", 111 | "is\n", 112 | "in\n", 113 | "a\n", 114 | "engineering\n", 115 | "describing\n", 116 | "authors\n", 117 | "are\n", 118 | "with\n", 119 | "two\n", 120 | "chapters\n", 121 | "been\n", 122 | "to\n", 123 | "as\n", 124 | "elements\n", 125 | "reusable\n", 126 | "s\n", 127 | "erich\n", 128 | "gamma\n", 129 | "richard\n", 130 | "helm\n", 131 | "ralph\n", 132 | "johnson\n", 133 | "john\n", 134 | "vlissides\n", 135 | "foreword\n", 136 | "by\n", 137 | "grady\n", 138 | "booch\n", 139 | "divided\n", 140 | "into\n", 141 | "parts\n", 142 | "first\n", 143 | "exploring\n", 144 | "capabilities\n", 145 | "pitfalls\n", 146 | "programming\n", 147 | "remaining\n", 148 | "23\n", 149 | "classic\n", 150 | "includes\n", 151 | "examples\n", 152 | "c\n", 153 | "smalltalk\n", 154 | "it\n", 155 | "has\n", 156 | "influential\n", 157 | "field\n", 158 | "regarded\n", 159 | "an\n", 160 | "important\n", 161 | "source\n", 162 | "for\n", 163 | "theory\n", 164 | "practice\n", 165 | "more\n", 166 | "than\n", 167 | "500\n", 168 | "000\n", 169 | "copies\n", 170 | "have\n", 171 | "sold\n", 172 | "english\n", 173 | "13\n", 174 | "other\n", 175 | "languages\n", 176 | "often\n", 177 | "referred\n", 178 | "gang\n", 179 | "four\n", 180 | "gof\n" 181 | ] 182 | } 183 | ], 184 | "source": [ 185 | "class WordCounterAdapter(TextProcessor): # Адаптер к обработчику\n", 186 | " def __init__(self, adaptee): # В конструкторе указывается, к какому объекту следует подключить адаптер \n", 187 | " self.adaptee = adaptee\n", 188 | " \n", 189 | " def process_text(self, text): # Реализация интерфейса обработчика, требуемого системой.\n", 190 | " self.adaptee.count_words(text)\n", 191 | " words = self.adaptee.get_all_words().keys()\n", 192 | " return sorted(words, key = lambda x: self.adaptee.get_count(x), reverse = True)\n", 193 | "\n", 194 | "system = System(text)\n", 195 | "counter = WordCounter()\n", 196 | "adapter = WordCounterAdapter(counter)\n", 197 | "system.get_processed_text(adapter)" 198 | ] 199 | } 200 | ], 201 | "metadata": { 202 | "kernelspec": { 203 | "display_name": "Python 3", 204 | "language": "python", 205 | "name": "python3" 206 | }, 207 | "language_info": { 208 | "codemirror_mode": { 209 | "name": "ipython", 210 | "version": 3 211 | }, 212 | "file_extension": ".py", 213 | "mimetype": "text/x-python", 214 | "name": "python", 215 | "nbconvert_exporter": "python", 216 | "pygments_lexer": "ipython3", 217 | "version": "3.6.3" 218 | } 219 | }, 220 | "nbformat": 4, 221 | "nbformat_minor": 2 222 | } 223 | -------------------------------------------------------------------------------- /week_3/Observer.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Паттерн Наблюдатель (Observer)\n", 8 | "\n", 9 | "Паттерн Наблюдатель является поведенческим паттерном проектирования. Он предназначен для организации взаимодействия между классами. Он реализует взаимодействия типа один-много, при котором множество объектов получают информацию об изменениях основного объекта. \n", 10 | "\n", 11 | "По данному принципу работает огромное количество приложений. Это могут быть новостные рассылки, уведомления от приложений на смартфонах, автоматическая рассылка почты, системы достижений в израх и многое другое.\n", 12 | "\n", 13 | "Вместо решения, при котором объект наблюдатель опрашивает наблюдаемый объект о произошедших изменениях, наблюдаймый объект самостоятельно уведомляет о них наблюдателя.\n", 14 | "\n", 15 | "В паттерне наблюдатель в наблюдаемой системе должен быть имплементирован интерфейс наблюдаемого объекта, позволяющий \"подписывать\" пользователя на обновления объекта и отправлять всем подписанным пользователям уведомления об изменениях. Также должны существовать наблюдатели, реализующие интерфейс наблюдателя. " 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "# Структура наблюдателя\n", 23 | "\n", 24 | "\n", 25 | "Для паттерна Observer необходимы следующие классы:\n", 26 | "\n", 27 | "* Абстрактный наблюдаемый объект\n", 28 | "* Абстрактный наблюдатель\n", 29 | "* Конкретный наблюдаемый объект\n", 30 | "* Конкретные наблюдатели\n", 31 | "\n", 32 | "У наблюдаемого объекта должны быть реализованы методы:\n", 33 | "\n", 34 | "* Подписать наблюдателя на обновления\n", 35 | "* Отписать от обновления\n", 36 | "* Уведомить всех подписчиков об изменениях\n", 37 | "\n", 38 | "У наблюдателя должен быть реализован метод update, который будет вызван наблюдаемым объектом при обновлении." 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "# Использование паттерна Наблюдатель\n", 46 | "\n", 47 | "При использовании паттерна Наблюдатель создаются наблюдатели и система. Для использования паттерна наблюдатель подписывается на обновления системы. При изменениях система оповещает об изменениях всех текущих подписчиков при помощи вызова у подписчиков метода update." 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": { 53 | "collapsed": true 54 | }, 55 | "source": [ 56 | "# Реализация паттерна Наблюдатеь" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 1, 62 | "metadata": { 63 | "collapsed": true 64 | }, 65 | "outputs": [], 66 | "source": [ 67 | "from abc import ABC, abstractmethod\n", 68 | "\n", 69 | "class NotificationManager: # Наблюдаемая система\n", 70 | " def __init__(self): \n", 71 | " self.__subscribers = set() # При инициализации множество подписчиков звдвется пустым\n", 72 | " \n", 73 | " def subscribe(self, subscriber):\n", 74 | " self.__subscribers.add(subscriber) # Для того чтобы подмисать пользователя, он добавляется во множество подписчиков\n", 75 | " \n", 76 | " def unsubcribe(self, subscriber):\n", 77 | " self.__subscribers.remove(subscriber) # Удаление подписчика из списка\n", 78 | " \n", 79 | " def notify(self, message):\n", 80 | " for subscriber in self.__subscribers:\n", 81 | " subscriber.update(message) # Отправка уведомления всем подписчикам" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 2, 87 | "metadata": { 88 | "collapsed": true 89 | }, 90 | "outputs": [], 91 | "source": [ 92 | "class AbstractObserver(ABC):\n", 93 | " @abstractmethod\n", 94 | " def update(self, message): # Абстрактный наблюдатель задает метод update \n", 95 | " pass\n", 96 | "\n", 97 | "class MessageNotifier(AbstractObserver):\n", 98 | " def __init__(self, name):\n", 99 | " self.__name = name\n", 100 | " \n", 101 | " def update(self, message): # Конкретная реализация метода update\n", 102 | " print(f'{self.__name} recieved message!')\n", 103 | " \n", 104 | "class MessagePrinter(AbstractObserver):\n", 105 | " def __init__(self, name):\n", 106 | " self.__name = name\n", 107 | " \n", 108 | " def update(self, message): # Конкретная реализация метода update\n", 109 | " print(f'{self.__name} recieved message: {message}')" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 3, 115 | "metadata": {}, 116 | "outputs": [ 117 | { 118 | "name": "stdout", 119 | "output_type": "stream", 120 | "text": [ 121 | "Printer2 recieved message: Hi!\n", 122 | "Printer1 recieved message: Hi!\n", 123 | "Notifier1 recieved message!\n" 124 | ] 125 | } 126 | ], 127 | "source": [ 128 | "notifier1 = MessageNotifier(\"Notifier1\")\n", 129 | "printer1 = MessagePrinter(\"Printer1\")\n", 130 | "printer2 = MessagePrinter(\"Printer2\")\n", 131 | "\n", 132 | "manager = NotificationManager()\n", 133 | "\n", 134 | "manager.subscribe(notifier1)\n", 135 | "manager.subscribe(printer1)\n", 136 | "manager.subscribe(printer2)\n", 137 | "\n", 138 | "manager.notify(\"Hi!\")" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": { 145 | "collapsed": true 146 | }, 147 | "outputs": [], 148 | "source": [] 149 | } 150 | ], 151 | "metadata": { 152 | "kernelspec": { 153 | "display_name": "Python 3", 154 | "language": "python", 155 | "name": "python3" 156 | }, 157 | "language_info": { 158 | "codemirror_mode": { 159 | "name": "ipython", 160 | "version": 3 161 | }, 162 | "file_extension": ".py", 163 | "mimetype": "text/x-python", 164 | "name": "python", 165 | "nbconvert_exporter": "python", 166 | "pygments_lexer": "ipython3", 167 | "version": "3.6.3" 168 | } 169 | }, 170 | "nbformat": 4, 171 | "nbformat_minor": 2 172 | } 173 | -------------------------------------------------------------------------------- /week_3/adapter.py: -------------------------------------------------------------------------------- 1 | # Реализуем адаптер для класса 2 | class MappingAdapter: 3 | 4 | def __init__(self, adaptee): 5 | # Сохраним адаптируемый объект 6 | self.adaptee = adaptee 7 | 8 | def lighten(self, grid): 9 | # Определим метод рассчета освещенности 10 | dim = (len(grid[0]), len(grid)) # Определение размера карты 11 | self.adaptee.set_dim(dim) # Установка размера карты в адаптируемом объекте 12 | # Инициализируем пустые списки препятствий и источников света 13 | obst = [] 14 | lght = [] 15 | # Считаем положения объектов с исходной карты 16 | for i in range(dim[0]): 17 | for j in range(dim[1]): 18 | if grid[j][i] == 1: 19 | lght.append((i, j)) 20 | elif grid[j][i] == -1: 21 | obst.append((i, j)) 22 | # Передадим положения объектов адаптируемому объекту 23 | self.adaptee.set_lights(lght) 24 | self.adaptee.set_obstacles(obst) 25 | # Вернем полученную карту освещенности 26 | return self.adaptee.grid 27 | -------------------------------------------------------------------------------- /week_3/decorator.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class Creature(ABC): 5 | @abstractmethod 6 | def feed(self): 7 | pass 8 | 9 | @abstractmethod 10 | def move(self): 11 | pass 12 | 13 | @abstractmethod 14 | def make_noise(self): 15 | pass 16 | 17 | 18 | class Animal(Creature): 19 | def feed(self): 20 | print("I eat grass") 21 | 22 | def move(self): 23 | print("I walk forward") 24 | 25 | def make_noise(self): 26 | print("WOOO!") 27 | 28 | 29 | class AbstractDecorator(Creature): 30 | def __init__(self, obj): 31 | """ 32 | Сохраняем базовый объект для нашего декоратора в переменную 33 | """ 34 | self.obj = obj 35 | 36 | def feed(self): 37 | self.obj.feed() 38 | 39 | def move(self): 40 | self.obj.move() 41 | 42 | def make_noise(self): 43 | self.obj.make_noise() 44 | 45 | 46 | class Swimming(AbstractDecorator): 47 | def move(self): 48 | print("I swim") 49 | 50 | def make_noise(self): 51 | print("...") 52 | 53 | 54 | class Predator(AbstractDecorator): 55 | def feed(self): 56 | print("I eat other animals") 57 | 58 | 59 | class Fast(AbstractDecorator): 60 | def move(self): 61 | self.obj.move() 62 | print("Fast!") 63 | 64 | 65 | if __name__ == "__main__": 66 | # Создадим базовое животное и выполним все его методы: 67 | animal = Animal() 68 | print(animal) 69 | animal.feed() 70 | animal.move() 71 | animal.make_noise() 72 | 73 | # Теперь сделаем из нашего животного водоплавающее. 74 | # Для этого определим, что оно является водоплавающим и 75 | # в качестве декорируемого объекта укажем наше животное. 76 | swimming = Swimming(animal) 77 | print(swimming) 78 | swimming.feed() 79 | swimming.move() 80 | swimming.make_noise() 81 | 82 | # Оно ест траву, плавает и не издаёт звуков. 83 | # Сделаем из нашего животного хищника: 84 | predator = Predator(animal) 85 | print(predator) 86 | predator.feed() 87 | predator.move() 88 | predator.make_noise() 89 | 90 | # Получили быстрого водоплавающего хищника. 91 | fast = Fast(animal) 92 | print(fast) 93 | fast.feed() 94 | fast.move() 95 | fast.make_noise() 96 | 97 | # можно применять несколько одинаковых декораторов к одному объекту. 98 | # Мы можем сделать наше животное ещё быстрее: 99 | faster = Fast(fast) 100 | print(faster) 101 | faster.feed() 102 | faster.move() 103 | faster.make_noise() 104 | 105 | # Base object w/o Decoratar 106 | print(faster.obj.obj) 107 | -------------------------------------------------------------------------------- /week_3/exercises/adaptee.md: -------------------------------------------------------------------------------- 1 | # Создание адаптера для класса 2 | 3 | Вы продолжаете писать игру, и настало время разобраться с расчетом освещенности на карте. Так как это не совсем тривиальная задача, вы хотели бы использовать готовое решение, а не писать свое собственное. Вам удалось найти готовый класс, который решает задачу, однако интерфейс этого класса не совместим с вашей игрой. 4 | 5 | Вам нужно написать адаптер, который позволил бы использовать найденный вами класс совместно с вашей системой. 6 | 7 | Интерфейс класса выглядит следующим образом: 8 | 9 | ```python 10 | class Light: 11 | def __init__(self, dim): 12 | self.dim = dim 13 | self.grid = [[0 for i in range(dim[0])] for _ in range(dim[1])] 14 | self.lights = [] 15 | self.obstacles = [] 16 | 17 | def set_dim(self, dim): 18 | self.dim = dim 19 | self.grid = [[0 for i in range(dim[0])] for _ in range(dim[1])] 20 | 21 | def set_lights(self, lights): 22 | self.lights = lights 23 | self.generate_lights() 24 | 25 | def set_obstacles(self, obstacles): 26 | self.obstacles = obstacles 27 | self.generate_lights() 28 | 29 | def generate_lights(self): 30 | return self.grid.copy() 31 | 32 | ``` 33 | 34 | Интерфейс системы выглядит следующим образом: 35 | 36 | ```python 37 | class System: 38 | def __init__(self): 39 | self.map = self.grid = [[0 for i in range(30)] for _ in range(20)] 40 | self.map[5][7] = 1 # Источники света 41 | self.map[5][2] = -1 # Стены 42 | 43 | def get_lightening(self, light_mapper): 44 | self.lightmap = light_mapper.lighten(self.map) 45 | 46 | ... 47 | ``` 48 | 49 | Класс `Light` создает в методе `__init__` поле заданного размера. За размер поля отвечает параметр, представляющий из себя кортеж из 2 чисел. Элемент `dim[1]` отвечает за высоту карты, `dim[0]` за ее ширину. Метод `set_lights` устанавливает массив источников света с заданными координатами и просчитывает освещение. Метод `set_obstacles` устанавливает препятствия аналогичным образом. Положение элементов задается списком кортежей. В каждом элементе кортежа хранятся 2 значения: `elem[0]` -- координата по ширине карты и `elem[1]` -- координата по высоте соответственно. Метод `generate_lights` рассчитывает освещенность с учетом источников и препятствий. 50 | 51 | Пример карты освещенности, полученной этим методом изображен на следующем рисунке: 52 | 53 | ![Light map](../../img/LightMap.jpeg) 54 | 55 | В системе в конструкторе создается двухмерная, карта, на которой источники света обозначены как `1`, а препятствия как `-1`. Метод `get_lightening` принимает в качестве аргумента объект, который должен посчитывать освещение. У объекта вызывается метод `lighten`, который принимает карту объектов и источников света и возвращает карту освещенности. 56 | 57 | Вам необходимо написать адаптер `MappingAdapter`. Прототип класса вам дан в качестве исходного кода. 58 | 59 | ```python 60 | class MappingAdapter: 61 | def __init__(self, adaptee): 62 | pass 63 | 64 | def lighten(self, grid): 65 | pas 66 | ``` 67 | -------------------------------------------------------------------------------- /week_3/exercises/adaptee.py: -------------------------------------------------------------------------------- 1 | class Light: 2 | """ 3 | Рассчёт освещенности на карте 4 | """ 5 | 6 | def __init__(self, dim): 7 | """ 8 | Создаем поле заданного размера: 9 | :type dim: Tuple[int, int] 10 | - width, height = dim[0], dim[1] 11 | """ 12 | self.dim = dim 13 | # n-мерный масив нулей 14 | self.grid = [[0 for i in range(dim[0])] 15 | for _ in range(dim[1])] 16 | self.lights = [] 17 | self.obstacles = [] 18 | 19 | def set_dim(self, dim): 20 | self.dim = dim 21 | self.grid = [[0 for i in range(dim[0])] 22 | for _ in range(dim[1])] 23 | 24 | def set_lights(self, lights): 25 | """ 26 | устанавливает массив источников света с заданными координатами 27 | и просчитывает освещение 28 | """ 29 | self.lights = lights 30 | self.generate_lights() 31 | 32 | def set_obstacles(self, obstacles): 33 | """ 34 | устанавливает препятствия аналогичным образом 35 | """ 36 | self.obstacles = obstacles 37 | self.generate_lights() 38 | 39 | def generate_lights(self): 40 | return self.grid.copy() 41 | 42 | 43 | class System: 44 | def __init__(self): 45 | self.map = self.grid = [[0 for i in range(30)] 46 | for _ in range(20)] 47 | self.map[5][7] = 1 # Источники света 48 | self.map[5][2] = -1 # Стены 49 | 50 | def get_lightening(self, light_mapper): 51 | self.lightmap = light_mapper.lighten(self.map) 52 | 53 | 54 | class MappingAdapter(Light): 55 | def __init__(self, adaptee): 56 | self.adaptee = adaptee 57 | 58 | def _get_lights_and_obstacles(self, grid): 59 | lights = [] 60 | obstacles = [] 61 | for i, row in enumerate(grid): 62 | for j, elem in enumerate(row): 63 | if elem == 1: 64 | lights += [(j, i)] 65 | if elem == -1: 66 | obstacles += [(j, i)] 67 | return lights, obstacles 68 | 69 | def lighten(self, grid): 70 | dim = (len(grid[0]), len(grid)) 71 | self.adaptee.set_dim(dim) 72 | lights, obstacles = self._get_lights_and_obstacles(grid) 73 | self.adaptee.set_lights(lights) 74 | self.adaptee.set_obstacles(obstacles) 75 | return self.adaptee.generate_lights() 76 | 77 | 78 | if __name__ == '__main__': 79 | system = System() 80 | lights = Light((0, 0)) 81 | adapter = MappingAdapter(lights) 82 | system.get_lightening(adapter) 83 | -------------------------------------------------------------------------------- /week_3/exercises/classes_Adapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_3/exercises/classes_Adapter.png -------------------------------------------------------------------------------- /week_3/exercises/classes_Decorator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_3/exercises/classes_Decorator.png -------------------------------------------------------------------------------- /week_3/exercises/deco.md: -------------------------------------------------------------------------------- 1 | # Создание декоратора класса 2 | 3 | Представьте себя ненадолго разработчиком компьютерной игры в стиле фэнтези. Вы будете прописывать систему эффектов, которые могут быть наложены на героя вашей игры. 4 | 5 | У вас есть герой, который обладает некоторым набором характеристик. Враги и союзники могут накладывать на героя положительные и отрицательные эффекты. Эти эффекты каким-то образом изменяют характеристики героя. На героя можно накладывать бесконечно много эффектов, действие одинаковых эффектов суммируется. Игрок должен знать, какие положительные и какие отрицательные эффекты на него были наложены и в каком порядке. 6 | 7 | Описывать класс героя в коде НЕ НУЖНО. 8 | 9 | Вам нужно написать систему декораторов, представленную на UML-диаграмме: 10 | 11 | ![Decorator UML](../../img/decorator_uml.jpeg) 12 | 13 | Названия наложенных положительных и отрицательных эффектов добавляются каждое в свой счетчик. Названия эффектов совпадают с названиями классов. 14 | 15 | Описания эффектов: 16 | 17 | - **Берсерк** — Увеличивает параметры Сила, Выносливость, Ловкость, Удача на 7; уменьшает параметры Восприятие, Харизма, Интеллект на 3. Количество единиц здоровья увеличивается на 50. 18 | - **Благословение** — Увеличивает все основные характеристики на 2. 19 | - **Слабость** — Уменьшает параметры Сила, Выносливость, Ловкость на 4. 20 | - **Сглаз** — Уменьшает параметр Удача на 10. 21 | - **Проклятье** — Уменьшает все основные характеристики на 2. 22 | 23 | К основным характеристикам относятся Сила (`Strength`), Восприятие (`Perception`), Выносливость (`Endurance`), Харизма (`Charisma`), Интеллект (`Intelligence`), Ловкость (`Agility`), Удача (`Luck`). 24 | 25 | При выполнении задания учитывайте, что: 26 | 27 | - Изначальные характеристики базового объекта не должны меняться. 28 | - Изменения характеристик и накладываемых эффектов (баффов/дебаффов) должно происходить динамически, то есть при запросе `get_stats`, `get_positive_effects`, `get_negative_effects` 29 | - Абстрактные классы `AbstractPositive`, `AbstractNegative` и соответственно их потомки могут принимать любой параметр base при инициализации объекта (`__init__(self, base))` 30 | - Проверяйте, что эффекты корректно снимаются, в том числе и из середины стека 31 | 32 | ```python 33 | >>> from deco import * 34 | >>> # создаем героя 35 | >>> hero = Hero() 36 | >>> hero.get_stats() 37 | {'HP': 128, 'MP': 42, 'SP': 100, 'Strength': 15, 'Perception': 4, 'Endurance': 8, 'Charisma': 2, 'Intelligence': 3, 'Agility': 8, 'Luck': 1} 38 | >>> hero.stats 39 | {'HP': 128, 'MP': 42, 'SP': 100, 'Strength': 15, 'Perception': 4, 'Endurance': 8, 'Charisma': 2, 'Intelligence': 3, 'Agility': 8, 'Luck': 1} 40 | >>> hero.get_negative_effects() 41 | [ ] 42 | >>> hero.get_positive_effects() 43 | [ ] 44 | >>> # накладываем эффект 45 | >>> brs1 = Berserk(hero) 46 | >>> brs1.get_stats() 47 | {'HP': 178, 'MP': 42, 'SP': 100, 'Strength': 22, 'Perception': 1, 'Endurance': 15, 'Charisma': -1, 'Intelligence': 0, 'Agility': 15, 'Luck': 8} 48 | >>> brs1.get_negative_effects() 49 | [ ] 50 | >>> brs1.get_positive_effects() 51 | ['Berserk'] 52 | >>> # накладываем эффекты 53 | >>> brs2 = Berserk(brs1) 54 | >>> cur1 = Curse(brs2) 55 | >>> cur1.get_stats() 56 | {'HP': 228, 'MP': 42, 'SP': 100, 'Strength': 27, 'Perception': -4, 'Endurance': 20, 'Charisma': -6, 'Intelligence': -5, 'Agility': 20, 'Luck': 13} 57 | >>> cur1.get_positive_effects() 58 | ['Berserk', 'Berserk'] 59 | >>> cur1.get_negative_effects() 60 | ['Curse'] 61 | >>> # снимаем эффект Berserk 62 | >>> cur1.base = brs1 63 | >>> cur1.get_stats() 64 | {'HP': 178, 'MP': 42, 'SP': 100, 'Strength': 20, 'Perception': -1, 'Endurance': 13, 'Charisma': -3, 'Intelligence': -2, 'Agility': 13, 'Luck': 6} 65 | >>> cur1.get_positive_effects() 66 | ['Berserk'] 67 | >>> cur1.get_negative_effects() 68 | ['Curse'] 69 | >>> 70 | ``` -------------------------------------------------------------------------------- /week_3/exercises/deco.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class Hero: 5 | def __init__(self): 6 | self.positive_effects = [] 7 | self.negative_effects = [] 8 | 9 | self.stats = { 10 | "HP": 128, 11 | "MP": 42, 12 | "SP": 100, 13 | 14 | "Strength": 15, 15 | "Perception": 4, 16 | "Endurance": 8, 17 | "Charisma": 2, 18 | "Intelligence": 3, 19 | "Agility": 8, 20 | "Luck": 1 21 | } 22 | 23 | def get_positive_effects(self): 24 | return self.positive_effects.copy() 25 | 26 | def get_negative_effects(self): 27 | return self.negative_effects.copy() 28 | 29 | def get_stats(self): 30 | return self.stats.copy() 31 | 32 | 33 | class AbstractEffect(Hero, ABC): 34 | 35 | def __init__(self, base): 36 | self.base = base 37 | 38 | @abstractmethod 39 | def get_positive_effects(self): 40 | return self.positive_effects 41 | 42 | @abstractmethod 43 | def get_negative_effects(self): 44 | return self.negative_effects 45 | 46 | @abstractmethod 47 | def get_stats(self): 48 | pass 49 | 50 | 51 | class AbstractPositive(AbstractEffect): 52 | """ 53 | :return: список наложенных отрицательных эффектов 54 | без изменений, чтобы не определять данный метод 55 | во всех положительных эффектах 56 | """ 57 | 58 | def get_negative_effects(self): 59 | return self.base.get_negative_effects() 60 | 61 | 62 | class Berserk(AbstractPositive): 63 | 64 | def get_stats(self): 65 | # Получим характеристики базового объекта, 66 | # модифицируем их и вернем 67 | stats = self.base.get_stats() 68 | stats["HP"] += 50 69 | stats["Strength"] += 7 70 | stats["Endurance"] += 7 71 | stats["Agility"] += 7 72 | stats["Luck"] += 7 73 | stats["Perception"] -= 3 74 | stats["Charisma"] -= 3 75 | stats["Intelligence"] -= 3 76 | return stats 77 | 78 | def get_positive_effects(self): 79 | # Модифицируем список эффектов, 80 | # добавив в него новый эффект 81 | return self.base.get_positive_effects() + ["Berserk"] 82 | 83 | 84 | class Blessing(AbstractPositive): 85 | 86 | def get_stats(self): 87 | stats = self.base.get_stats() 88 | stats["Strength"] += 2 89 | stats["Endurance"] += 2 90 | stats["Agility"] += 2 91 | stats["Luck"] += 2 92 | stats["Perception"] += 2 93 | stats["Charisma"] += 2 94 | stats["Intelligence"] += 2 95 | return stats 96 | 97 | def get_positive_effects(self): 98 | return self.base.get_positive_effects() + ["Blessing"] 99 | 100 | 101 | class AbstractNegative(AbstractEffect): 102 | """ 103 | Для отрицательных эффектов неизменным останется 104 | список положительных эффектов 105 | """ 106 | 107 | def get_positive_effects(self): 108 | return self.base.get_positive_effects() 109 | 110 | 111 | class Weakness(AbstractNegative): 112 | 113 | def get_stats(self): 114 | stats = self.base.get_stats() 115 | stats["Strength"] -= 4 116 | stats["Endurance"] -= 4 117 | stats["Agility"] -= 4 118 | return stats 119 | 120 | def get_negative_effects(self): 121 | return self.base.get_negative_effects() + ["Weakness"] 122 | 123 | 124 | class Curse(AbstractNegative): 125 | 126 | def get_stats(self): 127 | stats = self.base.get_stats() 128 | stats["Strength"] -= 2 129 | stats["Endurance"] -= 2 130 | stats["Agility"] -= 2 131 | stats["Luck"] -= 2 132 | stats["Perception"] -= 2 133 | stats["Charisma"] -= 2 134 | stats["Intelligence"] -= 2 135 | return stats 136 | 137 | def get_negative_effects(self): 138 | return self.base.get_negative_effects() + ["Curse"] 139 | 140 | 141 | class EvilEye(AbstractNegative): 142 | 143 | def get_stats(self): 144 | stats = self.base.get_stats() 145 | stats["Luck"] -= 10 146 | return stats 147 | 148 | def get_negative_effects(self): 149 | return self.base.get_negative_effects() + ["EvilEye"] 150 | -------------------------------------------------------------------------------- /week_3/exercises/observe.md: -------------------------------------------------------------------------------- 1 | # Паттерн Наблюдатель 2 | 3 | Продолжая работу над игрой, вы добрались до системы достижений. Иногда игре нужно наградить игрока за то, что он достигает определенного результата в игре. Это может быть, например, прохождение всех заданий в игре, достижение определенного уровня, совершение какого-то сложного действия и т.д. 4 | 5 | У каждой игры есть движок и интерфейс пользователя. Это два компонента, которые работают параллельно и взаимодействуют друг с другом. Достижения генерируются движком игры, а отображаются пользовательским интерфейсом. Кроме того, на современных игровых площадках, таких как _Steam_, _Google Play_, также отображаются достижения, полученные игроком. Для этого применяется как раз паттерн **Наблюдатель**. 6 | 7 | У вас есть движок `Engine`, который может создавать уведомления о достижениях. Вам необходимо написать обертку над движком, которая будет иметь возможность подписывать наблюдателей и рассылать им уведомления, а также иерархию наблюдателей. В иерархию наблюдателей должны входить _абстрактный наблюдатель_, `AbstractObserver`, от которого унаследованы 2 наблюдателя `ShortNotificationPrinter` и `FullNotificationPrinter`. Первый из них составляет множество названий полученных достижений, второй составляет список достижений в том порядке, в котором они даны в системе. Имейте в виду, что каждое достижение должно быть учтено только один раз. 8 | 9 | Иерархия классов приведена на следующей UML диаграмме: 10 | 11 | ![Engine Observer](../../img/EngineObserver.jpeg) 12 | 13 | Пример достижения, которое генерирует движок: 14 | 15 | ```python 16 | {"title": "Покоритель", "text": "Дается при выполнении всех заданий в игре"} 17 | ``` 18 | 19 | Метод `update` не должен возвращать никаких значений, он должен только изменять переменную `achievements`. 20 | 21 | Класс `Engine` реализовывать не нужно! 22 | 23 | -------------------------------------------------------------------------------- /week_3/exercises/observe.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class Engine: 5 | def __init__(self): 6 | self.subscribers = set() 7 | 8 | 9 | class ObservableEngine(Engine): 10 | 11 | def subscribe(self, subscriber): 12 | self.subscribers.add(subscriber) 13 | 14 | def unsubscribe(self, subscriber): 15 | self.subscribers.remove(subscriber) 16 | 17 | def notify(self, message): 18 | for subscriber in self.subscribers: 19 | subscriber.update(message) 20 | 21 | 22 | class AbstractObserver(ABC): 23 | @abstractmethod 24 | def update(self, message): 25 | pass 26 | 27 | 28 | class ShortNotificationPrinter(AbstractObserver): 29 | def __init__(self): 30 | # Объявим множество всех полученных достижений 31 | self.achievements = set() 32 | 33 | def update(self, message): 34 | # Добавим название достижения во множество достижений 35 | self.achievements.add(message["title"]) 36 | 37 | 38 | class FullNotificationPrinter(AbstractObserver): 39 | def __init__(self): 40 | self.achievements = list() 41 | 42 | def update(self, message): 43 | if message not in self.achievements: 44 | self.achievements.append(message) 45 | 46 | 47 | if __name__ == "__main__": 48 | observable = ObservableEngine() 49 | short_printer = ShortNotificationPrinter() 50 | full_printer = FullNotificationPrinter() 51 | 52 | observable.subscribe(short_printer) 53 | observable.subscribe(short_printer) 54 | observable.subscribe(full_printer) 55 | 56 | observable.notify({"title": "Покоритель", 57 | "text": "Дается при выполнении всех заданий в игре"}) 58 | observable.notify({"title": "Победитель", 59 | "text": "Дается при выполнении заданий в игре"}) 60 | observable.notify({"title": "Покоритель", 61 | "text": "Дается при выполнении всех заданий в игре"}) 62 | observable.notify({"title": "Вин", 63 | "text": "Дается в игре"}) 64 | 65 | print(short_printer.achievements) 66 | print(full_printer.achievements) 67 | -------------------------------------------------------------------------------- /week_3/observer.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class Engine: 5 | def __init__(self): 6 | self.subscribers = set() 7 | 8 | # Опишем наблюдаемую систему 9 | 10 | 11 | class ObservableEngine(Engine): 12 | def __init__(self): 13 | # Объявим пустое множество подписчиков 14 | self.subscribers = set() 15 | 16 | def subscribe(self, subscriber): 17 | # Добавим пользователя во множество подписчиков 18 | self.subscribers.add(subscriber) 19 | 20 | def unsubscribe(self, subscriber): 21 | # Если данный подписчик присутствует в списке подписчиков, 22 | # его можно удалить 23 | if subscriber in self.subscribers: 24 | self.subscribers.remove(subscriber) 25 | 26 | def notify(self, message): 27 | # Отправка уведомления всем подписчикам 28 | for subscriber in self.subscribers: 29 | subscriber.update(message) 30 | 31 | 32 | # Определим абстрактного наблюдателя 33 | class AbstractObserver(ABC): 34 | 35 | # Каждый конуретный наблюдатель должен будет реализовать метод update 36 | @abstractmethod 37 | def update(self, message): 38 | pass 39 | 40 | 41 | # Определим конкретных наблюдателей 42 | class ShortNotificationPrinter(AbstractObserver): 43 | def __init__(self): 44 | # Объявим множество всех полученных достижений 45 | self.achievements = set() 46 | 47 | def update(self, message): 48 | # Добавим название достижения во множество достижений 49 | self.achievements.add(message["title"]) 50 | 51 | 52 | class FullNotificationPrinter(AbstractObserver): 53 | def __init__(self): 54 | # Объявим список всех полученных достижений 55 | self.achievements = list() 56 | 57 | def update(self, message): 58 | # Если подобного достижения не было в списке, добавим его 59 | if message not in self.achievements: 60 | self.achievements.append(message) 61 | -------------------------------------------------------------------------------- /week_3/patterns.md: -------------------------------------------------------------------------------- 1 | # Паттерны проектирования 2 | ![the sacred elements of faith](../img/sacred_elements.png) 3 | 4 | ## Основные понятия 5 | 6 | **Паттерн проектирования (design pattern)** — повторяемая архитектурная конструкция, применяемая для решения часто встречающихся задач. 7 | 8 | Паттерны проектирования — это не готовые куски кода, написанные на некотором языке. Это некоторые общие архитектурные решения, которые можно применять при решении задач. Они требуют доработки под каждую конкретную задачу. 9 | 10 | При описании любого паттерна проектирования обычно говорят о следующих понятиях: 11 | 12 | - Задача, для решения которой применяется паттерн проектирования 13 | - Достоинства применения данного паттерна при решении задачи перед другими методами 14 | - Диаграмма классов, описывающая структуру паттерна 15 | - Пример реализации паттерна 16 | 17 | ## Классификация паттернов проектирования 18 | Существует большое количество различных паттернов проектирования. Для комфортной работы с ними, их можно разделить на группы. Первый естественный признак, по которому можно делить паттерны — это уровень абстракции. Самые низкоуровневые паттерны существуют на уровне языка программирования и описывают решение довольно узкой задачи средствами конкретного языка. Такие паттерны называются идиомами. 19 | 20 | Паттерны наиболее высокого уровня называют архитектурными шаблонами. Они описывают не просто решение некоторой конкретной задачи, а подход к созданию целой информационной системы. Архиектурные шаблоны абсолютно не привязаны к языку программирования и могут быть реализованы на практически любом языке. 21 | 22 | К архитектурным шаблонам относятся, например, такие шаблоны, как: 23 | 24 | - Model-View-Controller (MVC) 25 | - Model-View-Presenter 26 | - Model-View-View Model 27 | - Presentation-Abstraction-Control 28 | - Naked objects 29 | - Hierarchical Model-View-Controller 30 | - View-Interactor-Presenter-Entity-Routing (VIPER) 31 | 32 | Паттерны промежуточного уровня как раз обычно и называют паттернами проектирования. Однако, даже в этой категории находится огромное количество шаблонов. Их тоже необходимо классифицировать. 33 | 34 | Паттерны обычно делать на 3 основных категории: структурные, поведенческие и порождающие. Разберемся с каждой из категорий. 35 | 36 | ## Структурные шаблоны 37 | **Структурные шаблоны** модифицируют структуру объектов. Они могут служить для получения из классов более сложных структур или альтернативной реализации доступа к объектам. 38 | 39 | К структурным шаблонам относятся следующие шаблоны: 40 | 41 | - [Адаптер](https://refactoring.guru/uk/design-patterns/adapter) (Adapter) – взаимодействие несовместимых объектов 42 | - [Мост](https://refactoring.guru/uk/design-patterns/bridge) (Bridge) – разделение абстракции и реализации 43 | - [Компоновщик](https://refactoring.guru/uk/design-patterns/composite) (Composite) – агрегирование нескольких объектов в одну структуру 44 | - [Декоратор](https://refactoring.guru/uk/design-patterns/decorator) (Decorator) – динамическое создание дополнительного поведения объекта 45 | - [Фасад](https://refactoring.guru/uk/design-patterns/facade) (Facade) – сокрытие сложной структуры за одним объектом, являющимся общей точкой доступа 46 | - [Приспособленец](https://refactoring.guru/uk/design-patterns/flyweight) (Flyweight) – общий объект, имеющий различные свойства в разных местах программы 47 | - [Заместитель](https://refactoring.guru/uk/design-patterns/proxy) (Proxy) – контроль доступа к некоторому объекту 48 | 49 | ## Порождающие шаблоны 50 | **Порождающие шаблоны** используются при создании различных объектов. Они призваны разделить процесс создания объектов и использования их системой. Это может применяться для реализации способа создания объектов независимо от типа создаваемого объекта или для сокрытия процесса создания объекта от системы. 51 | 52 | К порождающим шаблонам относятся следующие паттерны: 53 | 54 | - [Абстрактная фабрика](https://refactoring.guru/uk/design-patterns/abstract-factory) (Abstract factory) — создание семейств взаимосвязанных объектов 55 | - [Строитель](https://refactoring.guru/uk/design-patterns/builder) (Builder) — сокрытие инициализации для сложного объекта 56 | - [Фабричный метод](https://refactoring.guru/uk/design-patterns/factory-method) (Fаctory method) — общий интерфейс создания экзкмпляров подклассов некоторого класса 57 | - Отложенная инициализация (Lazy initialization) — создание объекта только при доступе к нему 58 | - Пул одиночек/Объектный пул (Multiton/Object pool) — повторное использование сложных объектов вместо повторного создания 59 | - [Прототип](https://refactoring.guru/uk/design-patterns/prototype) (Prototype) — упрощение создания объекта за счет клонирования уже имеющегося 60 | - [Одиночка](https://refactoring.guru/uk/design-patterns/singleton) (Singleton) — объект, присутствующий в системе в единственоом экземпляре 61 | 62 | ## Поведенческие паттерны 63 | **Поведенческие паттерны** описывают способы реализации взаимодействия между объектами различных типов. При таком взаимодействии объекты часто могут решить гораздо более сложные задачи, чем могли бы решить по-отдельности. 64 | 65 | К поведенческим паттернам относятся такие паттерны, как: 66 | 67 | - [Цепочка обязанностей](https://refactoring.guru/uk/design-patterns/chain-of-responsibility) (Chain of Responsibility) — обработка данных несколькими объектами 68 | - Интерпретатор (Interpreter) — решение частой незначительно изменяющейся задачи 69 | - [Итератор](https://refactoring.guru/uk/design-patterns/iterator) (Iterator) — последовательный доступ к объекту-коллекции 70 | - [Хранитель](https://refactoring.guru/uk/design-patterns/memento) (Memento) — сохранение и восстановление объекта 71 | - [Наблюдатель](https://refactoring.guru/uk/design-patterns/observer) (Observer) — оповещение об изменении некоторого объекта 72 | - [Состояние](https://refactoring.guru/uk/design-patterns/state) (State) — изменение поведения в зависимости от состояния 73 | - [Стратегия](https://refactoring.guru/uk/design-patterns/strategy) (Strategy) — выбор из нескольких вариантов поведения объекта 74 | - [Посетитель](https://refactoring.guru/uk/design-patterns/visitor) (Visitor) — выполнение некоторой операции над группой различных объектов -------------------------------------------------------------------------------- /week_4/Abstract_PRACTIC.py: -------------------------------------------------------------------------------- 1 | # Abstract Factory 2 | # Практическая реализация паттерна 3 | 4 | 5 | class HeroFactory: 6 | class Hero: 7 | def __init__(self, name): 8 | self.name = name 9 | 10 | class Spell: 11 | pass 12 | 13 | class Weapon: 14 | pass 15 | 16 | @classmethod 17 | def create_hero(cls, name): 18 | return cls.Hero(name) 19 | 20 | @classmethod 21 | def create_spell(cls): 22 | return cls.Spell() 23 | 24 | @classmethod 25 | def create_weapon(cls): 26 | return cls.Weapon() 27 | 28 | 29 | class WarriorFactory(HeroFactory): 30 | class Hero: 31 | def __init__(self, name): 32 | self.name = name 33 | self.spell = None 34 | self.weapon = None 35 | 36 | def add_spell(self, spell): 37 | self.spell = spell 38 | 39 | def add_weapon(self, weapon): 40 | self.weapon = weapon 41 | 42 | def hit(self): 43 | print(f"Warrior {self.name} hits with {self.weapon.hit()}") 44 | 45 | def cast(self): 46 | print(f"Warrior {self.name} hits with {self.spell.cast()}") 47 | 48 | class Weapon: 49 | def hit(self): 50 | return "Claymore" 51 | 52 | class Spell: 53 | def cast(self): 54 | return "Power" 55 | 56 | 57 | class MageFactory(HeroFactory): 58 | class Hero: 59 | def __init__(self, name): 60 | self.name = name 61 | self.weapon = None 62 | self.armor = None 63 | self.spell = None 64 | 65 | def add_weapon(self, weapon): 66 | self.weapon = weapon 67 | 68 | def add_spell(self, spell): 69 | self.spell = spell 70 | 71 | def hit(self): 72 | print(f"Mage {self.name} uses {self.weapon.hit()}") 73 | self.weapon.hit() 74 | 75 | def cast(self): 76 | print(f"Mage {self.name} casts {self.spell.cast()}") 77 | self.spell.cast() 78 | 79 | class Weapon: 80 | def hit(self): 81 | return "Staff" 82 | 83 | class Spell: 84 | def cast(self): 85 | return "Fireball" 86 | 87 | 88 | class AssassinFactory(HeroFactory): 89 | class Hero: 90 | def __init__(self, name): 91 | self.name = name 92 | self.spell = None 93 | self.weapon = None 94 | self.armor = None 95 | 96 | def add_spell(self, spell): 97 | self.spell = spell 98 | 99 | def add_weapon(self, weapon): 100 | self.weapon = weapon 101 | 102 | def hit(self): 103 | print(f"Assassin {self.name} uses {self.weapon.hit()}") 104 | 105 | def cast(self): 106 | print(f"Assassin {self.name} cast {self.spell.cast()}") 107 | 108 | class Weapon: 109 | def hit(self): 110 | return "Dagger" 111 | 112 | class Spell: 113 | def cast(self): 114 | return "Invisibility" 115 | 116 | 117 | def create_hero(factory): 118 | hero = factory.create_hero("Nagibator") 119 | weapon = factory.create_weapon() 120 | spell = factory.create_spell() 121 | 122 | hero.add_weapon(weapon) 123 | hero.add_spell(spell) 124 | 125 | return hero 126 | 127 | 128 | if __name__ == "__main__": 129 | factory = MageFactory() 130 | player = create_hero(factory) 131 | player.cast() 132 | player.hit() 133 | 134 | factory = AssassinFactory() 135 | player = create_hero(factory) 136 | player.cast() 137 | player.hit() 138 | -------------------------------------------------------------------------------- /week_4/exercises/chain.md: -------------------------------------------------------------------------------- 1 | # Реализовать Chain of Responsibility 2 | 3 | Вам дан объект класса SomeObject, содержащего три поля: `integer_field`, `float_field` и `string_field`: 4 | 5 | ```python 6 | class SomeObject: 7 | def __init__(self): 8 | self.integer_field = 0 9 | self.float_field = 0.0 10 | self.string_field = "" 11 | ``` 12 | 13 | Необходимо реализовать: 14 | 15 | - `EventGet()` создаёт событие получения данных соответствующего типа 16 | - `EventSet()` создаёт событие изменения поля типа `type()` 17 | 18 | Необходимо реализовать классы `NullHandler`, `IntHandler`, `FloatHandler`, `StrHandler` так, чтобы можно было создать цепочку: 19 | 20 | ```python 21 | chain = IntHandler(FloatHandler(StrHandler(NullHandler()))) 22 | ``` 23 | 24 | - `Chain.handle(obj, EventGet(int))` — вернуть значение `obj.integer_field` 25 | - `Chain.handle(obj, EventGet(str))` — вернуть значение `obj.string_field` 26 | - `Chain.handle(obj, EventGet(float))` — вернуть значение `obj.float_field` 27 | - `Chain.handle(obj, EventSet(1))` — установить значение `obj.integer_field =1` 28 | - `Chain.handle(obj, EventSet(1.1))` — установить значение `obj.float_field = 1.1` 29 | - `Chain.handle(obj, EventSet("str"))` — установить значение `obj.string_field = "str"` 30 | 31 | Пример работы: 32 | 33 | ```python 34 | >>> obj = SomeObject() 35 | >>> obj.integer_field = 42 36 | >>> obj.float_field = 3.14 37 | >>> obj.string_field = "some text" 38 | >>> chain = IntHandler(FloatHandler(StrHandler(NullHandler))) 39 | >>> chain.handle(obj, EventGet(int)) 40 | 42 41 | >>> chain.handle(obj, EventGet(float)) 42 | 3.14 43 | >>> chain.handle(obj, EventGet(str)) 44 | 'some text' 45 | >>> chain.handle(obj, EventSet(100)) 46 | >>> chain.handle(obj, EventGet(int)) 47 | 100 48 | >>> chain.handle(obj, EventSet(0.5)) 49 | >>> chain.handle(obj, EventGet(float)) 50 | 0.5 51 | >>> chain.handle(obj, EventSet('new text')) 52 | >>> chain.handle(obj, EventGet(str)) 53 | 'new text' 54 | ``` -------------------------------------------------------------------------------- /week_4/exercises/chain.py: -------------------------------------------------------------------------------- 1 | class SomeObject: 2 | def __init__(self): 3 | self.integer_field = 0 4 | self.float_field = 0.0 5 | self.string_field = "" 6 | 7 | 8 | # ИДЕНТИФИКАТОРЫ СОБЫТИЙ 9 | INT, FLOAT, STR = int, float, str 10 | 11 | 12 | class EventGet: 13 | def __init__(self, value): 14 | self.kind = {int: INT, float: FLOAT, str: STR}[value] 15 | self.value = None 16 | 17 | 18 | class EventSet: 19 | def __init__(self, value): 20 | self.kind = {int: INT, float: FLOAT, str: STR}[type(value)] 21 | self.value = value 22 | 23 | 24 | class NullHandler: 25 | def __init__(self, succesor=None): 26 | """ 27 | Передаем следующее звено цепочки: 28 | """ 29 | self.__succesor = succesor 30 | 31 | def handle(self, obj, event): 32 | if self.__succesor is not None: 33 | # RETURN for moving on... 34 | return self.__succesor.handle(obj, event) 35 | 36 | 37 | class IntHandler(NullHandler): 38 | def handle(self, obj, event): 39 | if event.kind == INT: 40 | if event.value is None: 41 | return obj.integer_field 42 | else: 43 | obj.integer_field = event.value 44 | else: 45 | return super().handle(obj, event) 46 | 47 | 48 | class FloatHandler(NullHandler): 49 | def handle(self, obj, event): 50 | if event.kind == FLOAT: 51 | if event.value is None: 52 | return obj.float_field 53 | else: 54 | obj.float_field = event.value 55 | else: 56 | return super().handle(obj, event) 57 | 58 | 59 | class StrHandler(NullHandler): 60 | def handle(self, obj, event): 61 | if event.kind == STR: 62 | if event.value is None: 63 | return obj.string_field 64 | else: 65 | obj.string_field = event.value 66 | else: 67 | return super().handle(obj, event) 68 | 69 | 70 | if __name__ == "__main__": 71 | obj = SomeObject() 72 | 73 | obj.integer_field = 42 74 | obj.float_field = 3.14 75 | obj.string_field = "some text" 76 | 77 | chain = IntHandler(FloatHandler(StrHandler(NullHandler()))) 78 | 79 | print(chain.handle(obj, EventGet(int))) 80 | print(chain.handle(obj, EventGet(float))) 81 | print(chain.handle(obj, EventGet(str))) 82 | 83 | chain.handle(obj, EventSet(100)) 84 | chain.handle(obj, EventSet(0.5)) 85 | chain.handle(obj, EventSet('new text')) 86 | 87 | print(chain.handle(obj, EventGet(int))) 88 | print(chain.handle(obj, EventGet(float))) 89 | print(chain.handle(obj, EventGet(str))) 90 | -------------------------------------------------------------------------------- /week_4/exercises/fabric.md: -------------------------------------------------------------------------------- 1 | # Реализуйте абстрактную фабрику 2 | 3 | Вам даны шесть классов: `EasyMap`, `EasyObjects`, `MediumMap`, `MediumObjects`, `HardMap`, `HardObjects`. Каждый из классов генерирует карту и список объектов для неё. 4 | 5 | На их основе Вам необходимо создать абстрактную фабрику `AbstractLevel` c классовыми методами `get_map()` и `get_objects()`. Её реализации должны носить имена `EasyLevel`, `MediumLevel` и `HardLevel`. 6 | 7 | ![Abstract Fabric](../../img/AbstractFabric.png) 8 | 9 | ```python 10 | import random 11 | 12 | 13 | class EasyMap: 14 | 15 | def __init__(self): 16 | self._map = [[0 for j in range(5)] for i in range(5)] 17 | for i in range(5): 18 | for j in range(5): 19 | if i == 0 or j == 0 or i == 4 or j == 4: 20 | # граница карты 21 | self._map[j][i] = -1 22 | else: 23 | # случайная характеристика области 24 | self._map[j][i] = random.randint(0, 2) 25 | 26 | def get_map(self): 27 | return self._map 28 | 29 | 30 | class EasyObjects: 31 | 32 | def __init__(self): 33 | # размещаем переход на след. уровень 34 | self.objects = [('next_lvl', (2, 2))] 35 | 36 | def get_objects(self, map_obj): 37 | # размещаем противников 38 | for obj_name in ['rat']: 39 | coord = (random.randint(1, 3), random.randint(1, 3)) 40 | # ищем случайную свободную локацию 41 | intersect = True 42 | while intersect: 43 | intersect = False 44 | for obj in self.objects: 45 | if coord == obj[1]: 46 | intersect = True 47 | coord = (random.randint(1, 3), random.randint(1, 3)) 48 | 49 | self.objects.append((obj_name, coord)) 50 | 51 | return self.objects 52 | 53 | 54 | class MediumMap: 55 | 56 | def __init__(self): 57 | self._map = [[0 for j in range(8)] for i in range(8)] 58 | for i in range(8): 59 | for j in range(8): 60 | if i == 0 or j == 0 or i == 7 or j == 7: 61 | # граница карты 62 | self._map[j][i] = -1 63 | else: 64 | # случайная характеристика области 65 | self._map[j][i] = random.randint(0, 2) 66 | 67 | def get_map(self): 68 | return self._map 69 | 70 | 71 | class MediumObjects: 72 | 73 | def __init__(self): 74 | # размещаем переход на след. уровень 75 | self.objects = [('next_lvl', (4, 4))] 76 | 77 | def get_objects(self, map_obj): 78 | # размещаем врагов 79 | for obj_name in ['rat', 'snake']: 80 | coord = (random.randint(1, 6), random.randint(1, 6)) 81 | # ищем случайную свободную локацию 82 | intersect = True 83 | while intersect: 84 | intersect = False 85 | for obj in self.objects: 86 | if coord == obj[1]: 87 | intersect = True 88 | coord = (random.randint(1, 6), random.randint(1, 6)) 89 | 90 | self.objects.append((obj_name, coord)) 91 | 92 | return self.objects 93 | 94 | 95 | class HardMap: 96 | 97 | def __init__(self): 98 | self._map = [[0 for j in range(10)] for i in range(10)] 99 | for i in range(10): 100 | for j in range(10): 101 | if i == 0 or j == 0 or i == 9 or j == 9: 102 | # граница карты 103 | self._map[j][i] = -1 104 | else: 105 | # характеристика области (-1 для непроходимой обл.) 106 | self._map[j][i] = random.randint(-1, 8) 107 | 108 | def get_map(self): 109 | return self._map 110 | 111 | 112 | class HardObjects: 113 | 114 | def __init__(self): 115 | # размещаем переход на след. уровень 116 | self.objects = [('next_lvl', (5, 5))] 117 | 118 | def get_objects(self, map_obj): 119 | # размещаем врагов 120 | for obj_name in ['rat', 'snake']: 121 | coord = (random.randint(1, 8), random.randint(1, 8)) 122 | # ищем случайную свободную локацию 123 | intersect = True 124 | while intersect: 125 | intersect = False 126 | if map_obj.get_map()[coord[0]][coord[1]] == -1: 127 | intersect = True 128 | coord = (random.randint(1, 8), random.randint(1, 8)) 129 | continue 130 | bj in self.objects: 131 | if coord == obj[1]: 132 | intersect = True 133 | coord = (random.randint(1, 8), random.randint(1, 8)) 134 | 135 | self.objects.append((obj_name, coord)) 136 | 137 | .obje 138 | ``` -------------------------------------------------------------------------------- /week_4/exercises/fabric.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | class AbstractLevel: 5 | 6 | class Map: 7 | pass 8 | 9 | class Objects: 10 | pass 11 | 12 | @classmethod 13 | def get_map(cls): 14 | return cls.Map() 15 | 16 | @classmethod 17 | def get_objects(cls): 18 | return cls.Objects() 19 | 20 | 21 | class EasyLevel(AbstractLevel): 22 | class Map: 23 | def __init__(self): 24 | self._map = [[0 for j in range(5)] for i in range(5)] 25 | for i in range(5): 26 | for j in range(5): 27 | if i == 0 or j == 0 or i == 4 or j == 4: 28 | # граница карты 29 | self._map[j][i] = -1 30 | else: 31 | # случайная характеристика области 32 | self._map[j][i] = random.randint(0, 2) 33 | 34 | def get_map(self): 35 | return self._map 36 | 37 | class Objects: 38 | def __init__(self): 39 | # размещаем переход на след. уровень 40 | self.objects = [('next_lvl', (2, 2))] 41 | 42 | def get_objects(self, map_obj): 43 | # размещаем противников 44 | for obj_name in ['rat']: 45 | coord = (random.randint(1, 3), random.randint(1, 3)) 46 | # ищем случайную свободную локацию 47 | intersect = True 48 | while intersect: 49 | intersect = False 50 | for obj in self.objects: 51 | if coord == obj[1]: 52 | intersect = True 53 | coord = (random.randint(1, 3), 54 | random.randint(1, 3)) 55 | 56 | self.objects.append((obj_name, coord)) 57 | 58 | return self.objects 59 | 60 | 61 | class MediumLevel(AbstractLevel): 62 | class Map: 63 | def __init__(self): 64 | self._map = [[0 for j in range(8)] for i in range(8)] 65 | for i in range(8): 66 | for j in range(8): 67 | if i == 0 or j == 0 or i == 7 or j == 7: 68 | # граница карты 69 | self._map[j][i] = -1 70 | else: 71 | # случайная характеристика области 72 | self._map[j][i] = random.randint(0, 2) 73 | 74 | def get_map(self): 75 | return self._map 76 | 77 | class Objects: 78 | def __init__(self): 79 | self.objects = [('next_lvl', (4, 4))] 80 | 81 | def get_objects(self, map_obj): 82 | for obj_name in ['rat', 'snake']: 83 | coord = (random.randint(1, 6), random.randint(1, 6)) 84 | intersect = True 85 | while intersect: 86 | intersect = False 87 | for obj in self.objects: 88 | if coord == obj[1]: 89 | intersect = True 90 | coord = (random.randint(1, 6), 91 | random.randint(1, 6)) 92 | 93 | self.objects.append((obj_name, coord)) 94 | 95 | return self.objects 96 | 97 | 98 | class HardLevel(AbstractLevel): 99 | class Map: 100 | def __init__(self): 101 | self._map = [[0 for j in range(10)] for i in range(10)] 102 | for i in range(10): 103 | for j in range(10): 104 | if i == 0 or j == 0 or i == 9 or j == 9: 105 | # граница карты :: непроходимый участок карты 106 | self._map[j][i] = -1 107 | else: 108 | # случайная характеристика области 109 | self._map[j][i] = random.randint(-1, 8) 110 | 111 | def get_map(self): 112 | return self._map 113 | 114 | class Objects: 115 | def __init__(self): 116 | self.objects = [('next_lvl', (5, 5))] 117 | 118 | def get_objects(self, map_obj): 119 | for obj_name in ['rat', 'snake']: 120 | coord = (random.randint(1, 8), random.randint(1, 8)) 121 | intersect = True 122 | while intersect: 123 | intersect = False 124 | # Замінити на: map_obj.get_map()[coord[0]][coord[1]] 125 | if map_obj[coord[0]][coord[1]] == -1: 126 | intersect = True 127 | coord = (random.randint(1, 8), random.randint(1, 8)) 128 | continue 129 | for obj in self.objects: 130 | if coord == obj[1]: 131 | intersect = True 132 | coord = (random.randint(1, 8), 133 | random.randint(1, 8)) 134 | 135 | self.objects.append((obj_name, coord)) 136 | return self.objects 137 | 138 | 139 | def create_level(factory): 140 | map_obj = factory.get_map() 141 | objects_obj = factory.get_objects() 142 | 143 | return {"map": map_obj, "obj": objects_obj} 144 | 145 | 146 | if __name__ == "__main__": 147 | # Перевірка для Курсери 148 | # print(HardLevel.get_objects().get_objects(map)) 149 | 150 | # Реалізація здорової людини: 151 | level = create_level(HardLevel) 152 | 153 | _map = level["map"].get_map() 154 | _obj = level["obj"].get_objects(_map) 155 | 156 | print(_map, _obj, sep='\n') 157 | -------------------------------------------------------------------------------- /week_4/exercises/yaml.md: -------------------------------------------------------------------------------- 1 | # Парсинг YAML-файла 2 | 3 | ## Описание задания 4 | 5 | Вам необходимо модифицировать приложенный код так, чтобы два следующих кода были эквивалентны (приводили к одинаковому результату) 6 | 7 | ```python 8 | Lebels = yaml.load('''levels: 9 | - !easy_level {} 10 | - !medium_level 11 | enemy: ['rat'] 12 | - !hard_level 13 | enemy: [ 14 | - rat 15 | - snake 16 | - dragon 17 | enemy_count: 10'''); 18 | ``` 19 | 20 | ```python 21 | Levels = {'levels':[]} 22 | _map = EasyLevel.Map() 23 | _obj = EasyLevel.Objects() 24 | Levels['levels'].append({'map': _map, 'obj': _obj}) 25 | 26 | _map = MediumLevel.Map() 27 | _obj = MediumLevel.Objects() 28 | _obj.config = {'enemy':['rat']} 29 | Levels['levels'].append({'map': _map, 'obj': _obj}) 30 | 31 | _map = HardLevel.Map() 32 | _obj = Hard_Level.Objects() 33 | _obj.config = {'enemy: ['rat', 'snake', 'dragon'], 'enemy_count': 10} 34 | Levels['levels'].append({'map': _map, 'obj': _obj}) 35 | 36 | ``` 37 | 38 | То есть в **Исходный код**: 39 | 40 | ```python 41 | import random 42 | import yaml 43 | from abc import ABC 44 | 45 | 46 | class AbstractLevel(yaml.YAMLObject): 47 | 48 | @classmethod 49 | def get_map(cls): 50 | return cls.Map() 51 | 52 | @classmethod 53 | def get_objects(cls): 54 | return cls.Objects() 55 | 56 | class Map(ABC): 57 | pass 58 | 59 | class Objects(ABC): 60 | pass 61 | 62 | 63 | class EasyLevel(AbstractLevel): 64 | class Map: 65 | def __init__(self): 66 | self.Map = [[0 for _ in range(5)] for _ in range(5)] 67 | for i in range(5): 68 | for j in range(5): 69 | if i == 0 or j == 0 or i == 4 or j == 4: 70 | self.Map[j][i] = -1 # граница карты 71 | else: 72 | self.Map[j][i] = random.randint(0, 2) # случайная характеристика области 73 | 74 | def get_map(self): 75 | return self.Map 76 | 77 | class Objects: 78 | def __init__(self): 79 | self.objects = [('next_lvl', (2, 2))] 80 | self.config = {} 81 | 82 | def get_objects(self, _map): 83 | for obj_name in ['rat']: 84 | coord = (random.randint(1, 3), random.randint(1, 3)) 85 | intersect = True 86 | while intersect: 87 | intersect = False 88 | for obj in self.objects: 89 | if coord == obj[1]: 90 | intersect = True 91 | coord = (random.randint(1, 3), random.randint(1, 3)) 92 | 93 | self.objects.append((obj_name, coord)) 94 | 95 | return self.objects 96 | 97 | 98 | class MediumLevel(AbstractLevel): 99 | class Map: 100 | def __init__(self): 101 | self.Map = [[0 for _ in range(8)] for _ in range(8)] 102 | for i in range(8): 103 | for j in range(8): 104 | if i == 0 or j == 0 or i == 7 or j == 7: 105 | self.Map[j][i] = -1 # граница карты 106 | else: 107 | self.Map[j][i] = random.randint(0, 2) # случайная характеристика области 108 | 109 | def get_map(self): 110 | return self.Map 111 | 112 | class Objects: 113 | def __init__(self): 114 | self.objects = [('next_lvl', (4, 4))] 115 | self.config = {'enemy': []} 116 | 117 | def get_objects(self, _map): 118 | for obj_name in self.config['enemy']: 119 | coord = (random.randint(1, 6), random.randint(1, 6)) 120 | intersect = True 121 | while intersect: 122 | intersect = False 123 | for obj in self.objects: 124 | if coord == obj[1]: 125 | intersect = True 126 | coord = (random.randint(1, 6), random.randint(1, 6)) 127 | 128 | self.objects.append((obj_name, coord)) 129 | 130 | return self.objects 131 | 132 | 133 | class HardLevel(AbstractLevel): 134 | class Map: 135 | def __init__(self): 136 | self.Map = [[0 for _ in range(10)] for _ in range(10)] 137 | for i in range(10): 138 | for j in range(10): 139 | if i == 0 or j == 0 or i == 9 or j == 9: 140 | self.Map[j][i] = -1 # граница карты :: непроходимый участок карты 141 | else: 142 | self.Map[j][i] = random.randint(-1, 8) # случайная характеристика области 143 | 144 | def get_map(self): 145 | return self.Map 146 | 147 | class Objects: 148 | def __init__(self): 149 | self.objects = [('next_lvl', (5, 5))] 150 | self.config = {'enemy_count': 5, 'enemy': []} 151 | 152 | def get_objects(self, _map): 153 | for obj_name in self.config['enemy']: 154 | for tmp_int in range(self.config['enemy_count']): 155 | coord = (random.randint(1, 8), random.randint(1, 8)) 156 | intersect = True 157 | while intersect: 158 | intersect = False 159 | if _map[coord[0]][coord[1]] == -1: 160 | intersect = True 161 | coord = (random.randint(1, 8), random.randint(1, 8)) 162 | continue 163 | for obj in self.objects: 164 | if coord == obj[1]: 165 | intersect = True 166 | coord = (random.randint(1, 8), random.randint(1, 8)) 167 | 168 | self.objects.append((obj_name, coord)) 169 | 170 | return self.objects 171 | 172 | ``` -------------------------------------------------------------------------------- /week_4/exercises/yaml.py: -------------------------------------------------------------------------------- 1 | import random 2 | import yaml 3 | from abc import ABC 4 | 5 | 6 | class AbstractLevel(yaml.YAMLObject): 7 | 8 | @classmethod 9 | def get_map(cls): 10 | return cls.Map() 11 | 12 | @classmethod 13 | def get_objects(cls): 14 | return cls.Objects() 15 | 16 | class Map(ABC): 17 | pass 18 | 19 | class Objects(ABC): 20 | pass 21 | 22 | @classmethod 23 | def from_yaml(cls, loader, node): 24 | level_map = cls.get_map() 25 | level_objects = cls.get_objects() 26 | level_objects.config = loader.construct_mapping(node) 27 | 28 | return {"map": level_map, "obj": level_objects} 29 | 30 | 31 | class EasyLevel(AbstractLevel): 32 | yaml_tag = "!easy_level" 33 | 34 | class Map: 35 | def __init__(self): 36 | self.Map = [[0 for _ in range(5)] for _ in range(5)] 37 | for i in range(5): 38 | for j in range(5): 39 | if i == 0 or j == 0 or i == 4 or j == 4: 40 | self.Map[j][i] = -1 # граница карты 41 | else: 42 | self.Map[j][i] = random.randint(0, 2) 43 | # случайная характеристика области 44 | 45 | def get_map(self): 46 | return self.Map 47 | 48 | class Objects: 49 | def __init__(self): 50 | self.objects = [('next_lvl', (2, 2))] 51 | self.config = {} 52 | 53 | def get_objects(self, _map): 54 | for obj_name in ['rat']: 55 | coord = (random.randint(1, 3), random.randint(1, 3)) 56 | intersect = True 57 | while intersect: 58 | intersect = False 59 | for obj in self.objects: 60 | if coord == obj[1]: 61 | intersect = True 62 | coord = (random.randint(1, 3), 63 | random.randint(1, 3)) 64 | 65 | self.objects.append((obj_name, coord)) 66 | 67 | return self.objects 68 | 69 | 70 | class MediumLevel(AbstractLevel): 71 | yaml_tag = "!medium_level" 72 | 73 | class Map: 74 | def __init__(self): 75 | self.Map = [[0 for _ in range(8)] for _ in range(8)] 76 | for i in range(8): 77 | for j in range(8): 78 | if i == 0 or j == 0 or i == 7 or j == 7: 79 | self.Map[j][i] = -1 # граница карты 80 | else: 81 | self.Map[j][i] = random.randint(0, 2) 82 | # случайная характеристика области 83 | 84 | def get_map(self): 85 | return self.Map 86 | 87 | class Objects: 88 | def __init__(self): 89 | self.objects = [('next_lvl', (4, 4))] 90 | self.config = {'enemy': []} 91 | 92 | def get_objects(self, _map): 93 | for obj_name in self.config['enemy']: 94 | coord = (random.randint(1, 6), random.randint(1, 6)) 95 | intersect = True 96 | while intersect: 97 | intersect = False 98 | for obj in self.objects: 99 | if coord == obj[1]: 100 | intersect = True 101 | coord = (random.randint(1, 6), 102 | random.randint(1, 6)) 103 | 104 | self.objects.append((obj_name, coord)) 105 | 106 | return self.objects 107 | 108 | 109 | class HardLevel(AbstractLevel): 110 | yaml_tag = "!hard_level" 111 | 112 | class Map: 113 | def __init__(self): 114 | self.Map = [[0 for _ in range(10)] for _ in range(10)] 115 | for i in range(10): 116 | for j in range(10): 117 | if i == 0 or j == 0 or i == 9 or j == 9: 118 | self.Map[j][i] = -1 119 | # граница карты :: непроходимый участок карты 120 | else: 121 | self.Map[j][i] = random.randint(-1, 8) 122 | # случайная характеристика области 123 | 124 | def get_map(self): 125 | return self.Map 126 | 127 | class Objects: 128 | def __init__(self): 129 | self.objects = [('next_lvl', (5, 5))] 130 | self.config = {'enemy_count': 5, 'enemy': []} 131 | 132 | def get_objects(self, _map): 133 | for obj_name in self.config['enemy']: 134 | for tmp_int in range(self.config['enemy_count']): 135 | coord = (random.randint(1, 8), random.randint(1, 8)) 136 | intersect = True 137 | while intersect: 138 | intersect = False 139 | if _map[coord[0]][coord[1]] == -1: 140 | intersect = True 141 | coord = (random.randint(1, 8), 142 | random.randint(1, 8)) 143 | continue 144 | for obj in self.objects: 145 | if coord == obj[1]: 146 | intersect = True 147 | coord = (random.randint(1, 8), 148 | random.randint(1, 8)) 149 | 150 | self.objects.append((obj_name, coord)) 151 | 152 | return self.objects 153 | 154 | 155 | if __name__ == '__main__': 156 | Levels = {'levels': []} 157 | _map = EasyLevel.Map() 158 | _obj = EasyLevel.Objects() 159 | Levels['levels'].append({'map': _map, 'obj': _obj}) 160 | 161 | _map = MediumLevel.Map() 162 | _obj = MediumLevel.Objects() 163 | _obj.config = {'enemy': ['rat']} 164 | Levels['levels'].append({'map': _map, 'obj': _obj}) 165 | 166 | _map = HardLevel.Map() 167 | _obj = HardLevel.Objects() 168 | _obj.config = {'enemy': ['rat', 'snake', 'dragon'], 'enemy_count': 10} 169 | Levels['levels'].append({'map': _map, 'obj': _obj}) 170 | 171 | # Levels = yaml.load('''levels: 172 | # - !easy_level {} 173 | # - !medium_level 174 | # enemy: ['rat'] 175 | # - !hard_level 176 | # enemy: 177 | # - rat 178 | # - snake 179 | # - dragon 180 | # enemy_count: 10''') 181 | 182 | map = Levels["levels"][0]["map"].get_map() 183 | print(Levels["levels"][0]["obj"].get_objects(map)) 184 | -------------------------------------------------------------------------------- /week_4/yaml_START.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | 3 | hero_yaml = """ 4 | --- !Character 5 | factory: 6 | !factory assassin 7 | name: 8 | 7NaGiBaToR7 9 | """ 10 | 11 | 12 | class HeroFactory: 13 | class Hero: 14 | def __init__(self, name): 15 | self.name = name 16 | 17 | class Spell: 18 | pass 19 | 20 | class Weapon: 21 | pass 22 | 23 | @classmethod 24 | def create_hero(cls, name): 25 | return cls.Hero(name) 26 | 27 | @classmethod 28 | def create_spell(cls): 29 | return cls.Spell() 30 | 31 | @classmethod 32 | def create_weapon(cls): 33 | return cls.Weapon() 34 | 35 | 36 | class WarriorFactory(HeroFactory): 37 | class Hero: 38 | def __init__(self, name): 39 | self.name = name 40 | self.spell = None 41 | self.weapon = None 42 | 43 | def add_spell(self, spell): 44 | self.spell = spell 45 | 46 | def add_weapon(self, weapon): 47 | self.weapon = weapon 48 | 49 | def hit(self): 50 | print(f"Warrior {self.name} hits with {self.weapon.hit()}") 51 | 52 | def cast(self): 53 | print(f"Warrior {self.name} hits with {self.spell.cast()}") 54 | 55 | class Weapon: 56 | def hit(self): 57 | return "Claymore" 58 | 59 | class Spell: 60 | def cast(self): 61 | return "Power" 62 | 63 | 64 | class MageFactory(HeroFactory): 65 | class Hero: 66 | def __init__(self, name): 67 | self.name = name 68 | self.weapon = None 69 | self.armor = None 70 | self.spell = None 71 | 72 | def add_weapon(self, weapon): 73 | self.weapon = weapon 74 | 75 | def add_spell(self, spell): 76 | self.spell = spell 77 | 78 | def hit(self): 79 | print(f"Mage {self.name} uses {self.weapon.hit()}") 80 | self.weapon.hit() 81 | 82 | def cast(self): 83 | print(f"Mage {self.name} casts {self.spell.cast()}") 84 | self.spell.cast() 85 | 86 | class Weapon: 87 | def hit(self): 88 | return "Staff" 89 | 90 | class Spell: 91 | def cast(self): 92 | return "Fireball" 93 | 94 | 95 | class AssassinFactory(HeroFactory): 96 | class Hero: 97 | def __init__(self, name): 98 | self.name = name 99 | self.spell = None 100 | self.weapon = None 101 | self.armor = None 102 | 103 | def add_spell(self, spell): 104 | self.spell = spell 105 | 106 | def add_weapon(self, weapon): 107 | self.weapon = weapon 108 | 109 | def hit(self): 110 | print(f"Assassin {self.name} uses {self.weapon.hit()}") 111 | 112 | def cast(self): 113 | print(f"Assassin {self.name} cast {self.spell.cast()}") 114 | 115 | class Weapon: 116 | def hit(self): 117 | return "Dagger" 118 | 119 | class Spell: 120 | def cast(self): 121 | return "Invisibility" 122 | 123 | 124 | def factory_constructor(loader, node): 125 | data = loader.construct_scalar(node) 126 | if data == 'assassin': 127 | return AssassinFactory 128 | if data == 'mage': 129 | return MageFactory 130 | else: 131 | return WarriorFactory 132 | 133 | 134 | class Character(yaml.YAMLObject): 135 | yaml_tag = "!Character" 136 | 137 | def create_hero(self): 138 | hero = self.factory.create_hero(self.name) 139 | 140 | weapon = self.factory.create_weapon() 141 | spell = self.factory.create_spell() 142 | 143 | hero.add_weapon(weapon) 144 | hero.add_spell(spell) 145 | 146 | return hero 147 | 148 | 149 | if __name__ == "__main__": 150 | loader = yaml.Loader 151 | loader.add_constructor("!factory", factory_constructor) 152 | # На Курсере версия YAML устарела 153 | # правильный вариант от 154 | # coursera.org/learn/oop-patterns-python/discussions/weeks/4/threads/PTPtcn50EemiuA5t4WqocA 155 | hero = yaml.load(hero_yaml, yaml.Loader).create_hero() 156 | # https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation 157 | 158 | hero.hit() 159 | hero.cast() 160 | -------------------------------------------------------------------------------- /week_4/yaml_smpl/html.yml: -------------------------------------------------------------------------------- 1 | --- !HTMLreport 2 | # указывает, что хранящаяся ниже структура относиться к типу HTMLreport 3 | objects: 4 | - &img !img 5 | alt_text: google 6 | src: "https://blog.coursera.org/wp-content/uploads/2017/07/coursera-fb.png" 7 | report: !report 8 | filename: report_yaml.html 9 | title: Report 10 | parts: 11 | - !chapter 12 | caption: "chapter one" 13 | parts: 14 | - "chapter 1 text" 15 | - !link 16 | obj: coursera 17 | href: "https://ru.coursera.org" 18 | - !chapter 19 | caption: "chapter two" 20 | parts: 21 | - "Chapter 2 header" 22 | - !link 23 | obj: *img 24 | href: "https://ru.coursera.org" 25 | - "Chapter 2 footer" -------------------------------------------------------------------------------- /week_4/yaml_smpl/md.yml: -------------------------------------------------------------------------------- 1 | --- !MDreport 2 | # указывает, что хранящаяся ниже структура относиться к типу MDreport 3 | objects: # для хранения якорей 4 | - &img !img # якорь img хранит объект типа img 5 | alt_text: coursera # описание изображения 6 | src: "https://blog.coursera.org/wp-content/uploads/2017/07/coursera-fb.png" # адрес изображения 7 | report: !report # содержит непосредственно отчёт 8 | filename: report_yaml.md # имя файла отчёта 9 | title: !!str Report # название отчёта - строковый параметр (!!str) "Report" 10 | parts: 11 | # содержание отчёта - список частей (каждая часть начинаеться с "-") 12 | - !chapter 13 | # первая часть отчёта - объект типа "chapter" 14 | caption: "chapter one" # заглавие первой части 15 | parts: # содержание первой части - список ниже 16 | 17 | # первая часть - текст. 18 | # символ '>' вконце показывает, что весь блок ниже являеться содержанием. Перенос строк не учитываеться 19 | # Для учёта переноса строк - символ '|' 20 | 21 | - | 22 | chapter 23 | 1 24 | text 25 | - !link # далее ссылка 26 | obj: coursera # текст ссылки 27 | href: "https://ru.coursera.org" # куда ссылаеться 28 | - !chapter # вторая часть отчёта - объект типа "chapter" 29 | caption: "chapter two" # заглавие второй части 30 | parts: # содержание второй части - список ниже 31 | - "Chapter 2 header" # сначала текст 32 | - !link # далее ссылка 33 | obj: *img # объект, хранящийся по якорю img (изображение) будет являться ссылкой 34 | href: "https://ru.coursera.org" # куда ссылаеться 35 | - "Chapter 2 footer" # в конце - текст 36 | -------------------------------------------------------------------------------- /week_4/yaml_smpl/report_factory.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | 3 | 4 | def read_file(fname: str) -> str: 5 | with open(fname, 'r') as f: 6 | return f.read() 7 | 8 | 9 | yml_MD = read_file('md.yml') 10 | yml_HTML = read_file('html.yml') 11 | 12 | 13 | class ReportFactory(yaml.YAMLObject): 14 | """ 15 | теперь ReportFactory - потомок yaml.YAMLObject. 16 | Сделано для того, чтобы yaml оработчик знал новый тип данных, 17 | указанный в yaml_tag он будет определён в фабриках - потомках 18 | данные yaml фала - структура отчёта одинакова для всех потомков. 19 | В связи с этим - получение отчёта из yaml файла - классовый метод 20 | со специальным именем from_yaml 21 | """ 22 | 23 | @classmethod 24 | def from_yaml(cls, loader, node): 25 | # сначала опишем функции для обработки каждого нового типа 26 | 27 | def get_report(loader, node): 28 | # метод loader.construct_mapping() 29 | # формирует из содержания node словарь 30 | data = loader.construct_mapping(node) 31 | # обработчик создания отчёта !report 32 | rep = cls.make_report(data["title"]) 33 | rep.filename = data["filename"] 34 | # на данный момент data["parts"] пуст. 35 | # Он будет заполнен позже, соответствующим обработчиком, 36 | # сохраняем на него ссылку, дополнив сразу частями из rep.parts 37 | data["parts"].extend(rep.parts) 38 | rep.parts = data["parts"] 39 | return rep 40 | 41 | def get_chapter(loader, node): 42 | # обработчик создания части !chapter 43 | data = loader.construct_mapping(node) 44 | ch = cls.make_chapter(data["caption"]) 45 | # аналогично предыдущему обработчику 46 | data["parts"].extend(ch.objects) 47 | ch.objects = data["parts"] 48 | return ch 49 | 50 | def get_link(loader, node): 51 | # обработчик создания ссылки !link 52 | data = loader.construct_mapping(node) 53 | lnk = cls.make_link(data["obj"], data["href"]) 54 | return lnk 55 | 56 | def get_img(loader, node): 57 | # обработчик создания изображения !img 58 | data = loader.construct_mapping(node) 59 | img = cls.make_img(data["alt_text"], data["src"]) 60 | return img 61 | 62 | # добавляем обработчики 63 | loader.add_constructor(u"!report", get_report) 64 | loader.add_constructor(u"!chapter", get_chapter) 65 | loader.add_constructor(u"!link", get_link) 66 | loader.add_constructor(u"!img", get_img) 67 | 68 | # возвращаем результат yaml обработчика - отчёт 69 | return loader.construct_mapping(node)['report'] 70 | 71 | # ниже - без изменений 72 | 73 | @classmethod 74 | def make_report(cls, title): 75 | return cls.Report(title) 76 | 77 | @classmethod 78 | def make_chapter(cls, caption): 79 | return cls.Chapter(caption) 80 | 81 | @classmethod 82 | def make_link(cls, obj, href): 83 | return cls.Link(obj, href) 84 | 85 | @classmethod 86 | def make_img(cls, alt_text, src): 87 | return cls.Img(alt_text, src) 88 | 89 | # Далее берём непосредственно фабрики по производству элементов отчёта. 90 | # Добавляем соответствие фабрик yaml типу 91 | 92 | 93 | class MDreportFactory(ReportFactory): 94 | # указываем соответствие 95 | yaml_tag = u'!MDreport' 96 | 97 | class Report: 98 | def __init__(self, title): 99 | self.parts = [] 100 | self.parts.append("# "+title+"\n\n") 101 | 102 | self.filename = "" 103 | 104 | def add(self, part): 105 | self.parts.append(part) 106 | 107 | def save(self): 108 | # вносим изменения - имя файла отчёта указываеться в yaml файле 109 | try: 110 | file = open(self.filename, "w", encoding="utf-8") 111 | print('\n'.join(map(str, self.parts)), file=file) 112 | finally: 113 | if isinstance(self.filename, str) and file is not None: 114 | file.close() 115 | 116 | class Chapter: 117 | def __init__(self, caption): 118 | self.caption = caption 119 | self.objects = [] 120 | 121 | def add(self, obj): 122 | print(obj) 123 | self.objects.append(obj) 124 | 125 | def __str__(self): 126 | return f'## {self.caption}\n\n' + ''.join(map(str, self.objects)) 127 | 128 | class Link: 129 | def __init__(self, obj, href): 130 | self.obj = obj 131 | self.href = href 132 | 133 | def __str__(self): 134 | return f'[{self.obj}]({self.href})' 135 | 136 | class Img: 137 | def __init__(self, alt_text, src): 138 | self.alt_text = alt_text 139 | self.src = src 140 | 141 | def __str__(self): 142 | return f'![{self.alt_text}]({self.src})' 143 | 144 | 145 | class HTMLreportFactory(ReportFactory): 146 | yaml_tag = u'!HTMLreport' 147 | 148 | class Report: 149 | def __init__(self, title): 150 | self.title = title 151 | self.parts = [] 152 | self.parts.append("") 153 | self.parts.append("") 154 | self.parts.append("" + title + "") 155 | self.parts.append("") 156 | self.parts.append("") 157 | self.parts.append("") 158 | 159 | self.filename = "" 160 | 161 | def add(self, part): 162 | self.parts.append(part) 163 | 164 | def save(self): 165 | try: 166 | file = open(self.filename, "w", encoding="utf-8") 167 | print('\n'.join(map(str, self.parts)), file=file) 168 | finally: 169 | if isinstance(self.filename, str) and file is not None: 170 | file.close() 171 | 172 | class Chapter: 173 | def __init__(self, caption): 174 | self.caption = caption 175 | self.objects = [] 176 | 177 | def add(self, obj): 178 | self.objects.append(obj) 179 | 180 | def __str__(self): 181 | ch = f'

{self.caption}

' 182 | return ch + ''.join(map(str, self.objects)) 183 | 184 | class Link: 185 | def __init__(self, obj, href): 186 | self.obj = obj 187 | self.href = href 188 | 189 | def __str__(self): 190 | return f'{self.obj}' 191 | 192 | class Img: 193 | def __init__(self, alt_text, src): 194 | self.alt_text = alt_text 195 | self.src = src 196 | 197 | def __str__(self): 198 | return f'{self.alt_text}' 199 | 200 | 201 | if __name__ == "__main__": 202 | # Осталось провести загрузку yaml файла и вывести результат 203 | # загружаем yaml файл markdown отчёта 204 | txtreport = yaml.load(yml_MD) 205 | txtreport.save() # сохраняем 206 | print("Сохранено:", txtreport.filename) # вывод 207 | 208 | # загружаем yaml файл markdown отчёта 209 | HTMLreport = yaml.load(yml_HTML) 210 | HTMLreport.save() # сохраняем 211 | print("Сохранено:", HTMLreport.filename) # вывод 212 | -------------------------------------------------------------------------------- /week_5/final_project/.idea/final_project.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /week_5/final_project/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /week_5/final_project/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /week_5/final_project/Knight.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/Knight.pdf -------------------------------------------------------------------------------- /week_5/final_project/Knight.pyns: -------------------------------------------------------------------------------- 1 | # PynSource Version 1.2 2 | {'type':'meta', 'info1':'Lorem ipsum dolor sit amet, consectetur adipiscing elit is latin. Comments are saved.'} 3 | {'type':'umlshape', 'id':'AbstractObject', 'x':8, 'y':73, 'width':128, 'height':94, 'attrs': 'position|min_x|sprite', 'meths': 'draw'} 4 | {'type':'umlshape', 'id':'Interactive', 'x':137, 'y':7, 'width':102, 'height':24, 'attrs': '', 'meths': ''} 5 | {'type':'umlshape', 'id':'Ally', 'x':167, 'y':87, 'width':58, 'height':104, 'attrs': 'position|action|sprite', 'meths': '__init__|interact'} 6 | {'type':'umlshape', 'id':'Creature', 'x':160, 'y':209, 'width':77, 'height':124, 'attrs': 'max_hp|stats|sprite|hp|position', 'meths': '__init__|calc_max_HP'} 7 | {'type':'umlshape', 'id':'Enemy', 'x':269, 'y':66, 'width':58, 'height':124, 'attrs': 'xp|stats|sprite|hp|position', 'meths': '__init__|interact'} 8 | {'type':'umlshape', 'id':'Hero', 'x':32, 'y':355, 'width':82, 'height':144, 'attrs': 'gold|hp|level|exp|min_x', 'meths': '__init__|exp_interact|level_up|draw'} 9 | {'type':'umlshape', 'id':'Effect', 'x':170, 'y':345, 'width':60, 'height':164, 'attrs': 'max_hp|gold|stats|base|hp|sprite|level|exp|position|min_x', 'meths': '__init__'} 10 | {'type':'umlshape', 'id':'Berserk', 'x':266, 'y':331, 'width':82, 'height':54, 'attrs': '', 'meths': 'apply_effect'} 11 | {'type':'umlshape', 'id':'Blessing', 'x':261, 'y':239, 'width':82, 'height':54, 'attrs': '', 'meths': 'apply_effect'} 12 | {'type':'umlshape', 'id':'Weakness', 'x':269, 'y':417, 'width':82, 'height':54, 'attrs': '', 'meths': 'apply_effect'} 13 | {'type':'umlshape', 'id':'Curse', 'x':271, 'y':511, 'width':82, 'height':54, 'attrs': '', 'meths': 'apply_effect'} 14 | {'type':'umlshape', 'id':'ABC', 'x':56, 'y':11, 'width':35, 'height':24, 'attrs': '', 'meths': ''} 15 | {'type':'umlshape', 'id':'ScreenHandle', 'x':652, 'y':260, 'width':111, 'height':104, 'attrs': 'successor|next_coord', 'meths': '__init__|draw|connect_engine'} 16 | {'type':'umlshape', 'id':'GameSurface', 'x':850, 'y':324, 'width':102, 'height':124, 'attrs': 'engine', 'meths': 'draw_map|draw_object|calculate|draw_hero|draw|connect_engine'} 17 | {'type':'umlshape', 'id':'ProgressBar', 'x':472, 'y':427, 'width':102, 'height':94, 'attrs': 'engine', 'meths': '__init__|draw|connect_engine'} 18 | {'type':'umlshape', 'id':'InfoWindow', 'x':873, 'y':602, 'width':94, 'height':124, 'attrs': 'data|len|engine', 'meths': '__init__|draw|connect_engine|update'} 19 | {'type':'umlshape', 'id':'HelpWindow', 'x':660, 'y':424, 'width':94, 'height':114, 'attrs': 'data|len|engine', 'meths': '__init__|draw|connect_engine'} 20 | {'type':'umlshape', 'id':'pygame.Surface', 'x':437, 'y':299, 'width':128, 'height':24, 'attrs': '', 'meths': ''} 21 | {'type':'umlshape', 'id':'collections.deque', 'x':629, 'y':652, 'width':153, 'height':24, 'attrs': '', 'meths': ''} 22 | {'type':'umlshape', 'id':'MapFactory', 'x':512, 'y':39, 'width':94, 'height':24, 'attrs': '', 'meths': ''} 23 | {'type':'umlshape', 'id':'Map', 'x':440, 'y':105, 'width':58, 'height':84, 'attrs': 'Map', 'meths': '__init__|get_map'} 24 | {'type':'umlshape', 'id':'Objects', 'x':53, 'y':618, 'width':76, 'height':84, 'attrs': 'objects', 'meths': '__init__|get_objects'} 25 | {'type':'umlshape', 'id':'Empty_Map', 'x':661, 'y':172, 'width':86, 'height':54, 'attrs': 'yaml_tag', 'meths': ''} 26 | {'type':'umlshape', 'id':'Special_Map', 'x':654, 'y':23, 'width':102, 'height':54, 'attrs': 'yaml_tag', 'meths': ''} 27 | {'type':'umlshape', 'id':'EndMap', 'x':531, 'y':104, 'width':60, 'height':54, 'attrs': 'yaml_tag', 'meths': ''} 28 | {'type':'umlshape', 'id':'RandomMap', 'x':660, 'y':94, 'width':86, 'height':54, 'attrs': 'yaml_tag', 'meths': ''} 29 | {'type':'umlshape', 'id':'yaml.YAMLObject', 'x':343, 'y':38, 'width':136, 'height':24, 'attrs': '', 'meths': ''} 30 | {'type':'umlshape', 'id':'Objects.Ally', 'x':211, 'y':646, 'width':111, 'height':24, 'attrs': '', 'meths': ''} 31 | {'type':'umlshape', 'id':'Objects.Enemy', 'x':32, 'y':545, 'width':119, 'height':24, 'attrs': '', 'meths': ''} 32 | {'type':'umlshape', 'id':'GameEngine', 'x':855, 'y':28, 'width':94, 'height':274, 'attrs': 'objects|map|hero|level|working|subscribers|score|game_process|show_help', 'meths': 'subscribe|unsubscribe|notify|add_hero|interact|move_up|move_down|move_left|move_right|load_map|add_object|add_objects|delete_object'} 33 | {'type':'edge', 'id':'Ally_to_Interactive', 'source':'Ally', 'target':'Interactive', 'uml_edge_type': 'generalisation'} 34 | {'type':'edge', 'id':'AbstractObject_to_ABC', 'source':'AbstractObject', 'target':'ABC', 'uml_edge_type': 'generalisation'} 35 | {'type':'edge', 'id':'Enemy_to_Creature', 'source':'Enemy', 'target':'Creature', 'uml_edge_type': 'generalisation'} 36 | {'type':'edge', 'id':'Weakness_to_Effect', 'source':'Weakness', 'target':'Effect', 'uml_edge_type': 'generalisation'} 37 | {'type':'edge', 'id':'Curse_to_Effect', 'source':'Curse', 'target':'Effect', 'uml_edge_type': 'generalisation'} 38 | {'type':'edge', 'id':'Hero_to_Creature', 'source':'Hero', 'target':'Creature', 'uml_edge_type': 'generalisation'} 39 | {'type':'edge', 'id':'Creature_to_AbstractObject', 'source':'Creature', 'target':'AbstractObject', 'uml_edge_type': 'generalisation'} 40 | {'type':'edge', 'id':'Effect_to_Hero', 'source':'Effect', 'target':'Hero', 'uml_edge_type': 'generalisation'} 41 | {'type':'edge', 'id':'Ally_to_AbstractObject', 'source':'Ally', 'target':'AbstractObject', 'uml_edge_type': 'generalisation'} 42 | {'type':'edge', 'id':'Berserk_to_Effect', 'source':'Berserk', 'target':'Effect', 'uml_edge_type': 'generalisation'} 43 | {'type':'edge', 'id':'Blessing_to_Effect', 'source':'Blessing', 'target':'Effect', 'uml_edge_type': 'generalisation'} 44 | {'type':'edge', 'id':'Interactive_to_ABC', 'source':'Interactive', 'target':'ABC', 'uml_edge_type': 'generalisation'} 45 | {'type':'edge', 'id':'Enemy_to_Interactive', 'source':'Enemy', 'target':'Interactive', 'uml_edge_type': 'generalisation'} 46 | {'type':'edge', 'id':'ScreenHandle_to_pygame.Surface', 'source':'ScreenHandle', 'target':'pygame.Surface', 'uml_edge_type': 'generalisation'} 47 | {'type':'edge', 'id':'ProgressBar_to_ScreenHandle', 'source':'ProgressBar', 'target':'ScreenHandle', 'uml_edge_type': 'generalisation'} 48 | {'type':'edge', 'id':'InfoWindow_to_ScreenHandle', 'source':'InfoWindow', 'target':'ScreenHandle', 'uml_edge_type': 'generalisation'} 49 | {'type':'edge', 'id':'GameSurface_to_ScreenHandle', 'source':'GameSurface', 'target':'ScreenHandle', 'uml_edge_type': 'generalisation'} 50 | {'type':'edge', 'id':'HelpWindow_to_ScreenHandle', 'source':'HelpWindow', 'target':'ScreenHandle', 'uml_edge_type': 'generalisation'} 51 | {'type':'edge', 'id':'collections.deque_to_InfoWindow', 'source':'collections.deque', 'target':'InfoWindow', 'uml_edge_type': 'composition'} 52 | {'type':'edge', 'id':'collections.deque_to_HelpWindow', 'source':'collections.deque', 'target':'HelpWindow', 'uml_edge_type': 'composition'} 53 | {'type':'edge', 'id':'MapFactory_to_yaml.YAMLObject', 'source':'MapFactory', 'target':'yaml.YAMLObject', 'uml_edge_type': 'generalisation'} 54 | {'type':'edge', 'id':'RandomMap_to_MapFactory', 'source':'RandomMap', 'target':'MapFactory', 'uml_edge_type': 'generalisation'} 55 | {'type':'edge', 'id':'EndMap_to_MapFactory', 'source':'EndMap', 'target':'MapFactory', 'uml_edge_type': 'generalisation'} 56 | {'type':'edge', 'id':'Empty_Map_to_MapFactory', 'source':'Empty_Map', 'target':'MapFactory', 'uml_edge_type': 'generalisation'} 57 | {'type':'edge', 'id':'Special_Map_to_MapFactory', 'source':'Special_Map', 'target':'MapFactory', 'uml_edge_type': 'generalisation'} 58 | {'type':'edge', 'id':'Objects.Ally_to_Objects', 'source':'Objects.Ally', 'target':'Objects', 'uml_edge_type': 'composition'} 59 | {'type':'edge', 'id':'Objects.Enemy_to_Objects', 'source':'Objects.Enemy', 'target':'Objects', 'uml_edge_type': 'composition'} 60 | -------------------------------------------------------------------------------- /week_5/final_project/Logic.py: -------------------------------------------------------------------------------- 1 | import Service 2 | 3 | 4 | class GameEngine: 5 | objects = [] 6 | map = None 7 | hero = None 8 | level = -1 9 | working = True 10 | subscribers = set() 11 | score = 0. 12 | game_process = True 13 | show_help = False 14 | 15 | def subscribe(self, obj): 16 | self.subscribers.add(obj) 17 | 18 | def unsubscribe(self, obj): 19 | if obj in self.subscribers: 20 | self.subscribers.remove(obj) 21 | 22 | def notify(self, message): 23 | for i in self.subscribers: 24 | i.update(message) 25 | 26 | # HERO 27 | def add_hero(self, hero): 28 | self.hero = hero 29 | 30 | def interact(self): 31 | for obj in self.objects: 32 | if list(obj.position) == self.hero.position: 33 | self.delete_object(obj) 34 | obj.interact(self, self.hero) 35 | 36 | # MOVEMENT 37 | def move_up(self): 38 | self.score -= 0.02 39 | _up = self.map[self.hero.position[1] - 1][self.hero.position[0]] 40 | if _up == Service.wall: 41 | return 42 | self.hero.position[1] -= 1 43 | self.interact() 44 | 45 | def move_down(self): 46 | self.score -= 0.02 47 | _down = self.map[self.hero.position[1] + 1][self.hero.position[0]] 48 | if _down == Service.wall: 49 | return 50 | self.hero.position[1] += 1 51 | self.interact() 52 | 53 | def move_left(self): 54 | self.score -= 0.02 55 | _left = self.map[self.hero.position[1]][self.hero.position[0] - 1] 56 | if _left == Service.wall: 57 | return 58 | self.hero.position[0] -= 1 59 | self.interact() 60 | 61 | def move_right(self): 62 | self.score -= 0.02 63 | _right = self.map[self.hero.position[1]][self.hero.position[0] + 1] 64 | if _right == Service.wall: 65 | return 66 | self.hero.position[0] += 1 67 | self.interact() 68 | 69 | # MAP 70 | def load_map(self, game_map): 71 | self.map = game_map 72 | 73 | # OBJECTS 74 | def add_object(self, obj): 75 | self.objects.append(obj) 76 | 77 | def add_objects(self, objects): 78 | if objects is not None: 79 | self.objects.extend(objects) 80 | 81 | def delete_object(self, obj): 82 | self.objects.remove(obj) 83 | -------------------------------------------------------------------------------- /week_5/final_project/Main.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import os 3 | import Objects 4 | import ScreenEngine as SE 5 | import Logic 6 | import Service 7 | 8 | # Дополнительные задачи не сделаны. 9 | # PyYAML последней версии, потому `file.read(), Loader=yaml.Loader` 10 | 11 | SCREEN_DIM = (800, 600) 12 | 13 | pygame.init() 14 | gameDisplay = pygame.display.set_mode(SCREEN_DIM) 15 | pygame.display.set_caption("MyRPG") 16 | KEYBOARD_CONTROL = True 17 | 18 | if not KEYBOARD_CONTROL: 19 | import numpy as np 20 | answer = np.zeros(4, dtype=float) 21 | 22 | base_stats = { 23 | "strength": 20, 24 | "endurance": 20, 25 | "intelligence": 5, 26 | "luck": 5 27 | } 28 | 29 | 30 | def create_game(sprite_size, is_new): 31 | global hero, engine, drawer, iteration 32 | if is_new: 33 | hero = Objects.Hero(base_stats, Service.create_sprite( 34 | os.path.join("texture", "Hero.png"), sprite_size)) 35 | engine = Logic.GameEngine() 36 | Service.service_init(sprite_size) 37 | Service.reload_game(engine, hero) 38 | 39 | drawer = SE.GameSurface( 40 | (640, 480), pygame.SRCALPHA, (0, 480), 41 | SE.ProgressBar( 42 | (640, 120), (640, 0), 43 | SE.InfoWindow( 44 | (160, 600), (50, 50), 45 | SE.HelpWindow( 46 | (700, 500), pygame.SRCALPHA, (0, 0), 47 | SE.ScreenHandle( 48 | (0, 0)) 49 | )))) 50 | 51 | else: 52 | engine.sprite_size = sprite_size 53 | hero.sprite = Service.create_sprite( 54 | os.path.join("texture", "Hero.png"), sprite_size) 55 | Service.service_init(sprite_size, False) 56 | 57 | Logic.GameEngine.sprite_size = sprite_size 58 | 59 | drawer.connect_engine(engine) 60 | 61 | iteration = 0 62 | 63 | 64 | size = 60 65 | create_game(size, True) 66 | 67 | while engine.working: 68 | 69 | if KEYBOARD_CONTROL: 70 | for event in pygame.event.get(): 71 | if event.type == pygame.QUIT: 72 | engine.working = False 73 | if event.type == pygame.KEYDOWN: 74 | if event.key == pygame.K_h: 75 | engine.show_help = not engine.show_help 76 | if event.key == pygame.K_KP_PLUS: 77 | size = size + 1 78 | create_game(size, False) 79 | if event.key == pygame.K_KP_MINUS: 80 | size = size - 1 81 | create_game(size, False) 82 | if event.key == pygame.K_r: 83 | create_game(size, True) 84 | 85 | if event.key == pygame.K_ESCAPE: 86 | engine.working = False 87 | if engine.game_process: 88 | if event.key == pygame.K_UP: 89 | engine.move_up() 90 | iteration += 1 91 | elif event.key == pygame.K_DOWN: 92 | engine.move_down() 93 | iteration += 1 94 | elif event.key == pygame.K_LEFT: 95 | engine.move_left() 96 | iteration += 1 97 | elif event.key == pygame.K_RIGHT: 98 | engine.move_right() 99 | iteration += 1 100 | else: 101 | if event.key == pygame.K_RETURN: 102 | create_game() 103 | 104 | else: 105 | for event in pygame.event.get(): 106 | if event.type == pygame.QUIT: 107 | engine.working = False 108 | if engine.game_process: 109 | actions = [ 110 | engine.move_right, 111 | engine.move_left, 112 | engine.move_up, 113 | engine.move_down, 114 | ] 115 | answer = np.random.randint(0, 100, 4) 116 | prev_score = engine.score 117 | move = actions[np.argmax(answer)]() 118 | state = pygame.surfarray.array3d(gameDisplay) 119 | reward = engine.score - prev_score 120 | print(reward) 121 | else: 122 | create_game() 123 | 124 | gameDisplay.blit(drawer, (0, 0)) 125 | drawer.draw(gameDisplay) 126 | 127 | pygame.display.update() 128 | 129 | pygame.display.quit() 130 | pygame.quit() 131 | exit(0) 132 | -------------------------------------------------------------------------------- /week_5/final_project/Objects.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | import pygame 3 | import random 4 | 5 | 6 | def create_sprite(img, sprite_size): 7 | icon = pygame.image.load(img).convert_alpha() 8 | icon = pygame.transform.scale(icon, (sprite_size, sprite_size)) 9 | sprite = pygame.Surface((sprite_size, sprite_size), pygame.HWSURFACE) 10 | sprite.blit(icon, (0, 0)) 11 | return sprite 12 | 13 | 14 | class AbstractObject(ABC): 15 | @abstractmethod 16 | def __init__(self): 17 | self.sprite = None 18 | self.position = None 19 | self.min_x = self.min_y = 0 20 | 21 | def draw(self, display): 22 | sprite_size = self.sprite.get_size()[0] 23 | display.blit(self.sprite, [(self.position[0] - 5) * sprite_size, 24 | (self.position[1] - 5) * sprite_size]) 25 | 26 | 27 | class Interactive(ABC): 28 | 29 | @abstractmethod 30 | def interact(self, engine, hero): 31 | pass 32 | 33 | 34 | class Ally(AbstractObject, Interactive): 35 | 36 | def __init__(self, icon, action, position): 37 | self.sprite = icon 38 | self.action = action 39 | self.position = position 40 | 41 | def interact(self, engine, hero): 42 | self.action(engine, hero) 43 | 44 | 45 | class Creature(AbstractObject): 46 | 47 | def __init__(self, icon, stats, position): 48 | self.sprite = icon 49 | self.stats = stats 50 | self.position = position 51 | self.calc_max_HP() 52 | self.hp = self.max_hp 53 | 54 | def calc_max_HP(self): 55 | self.max_hp = 5 + self.stats["endurance"] * 2 56 | 57 | 58 | class Enemy(Creature, Interactive): 59 | 60 | def __init__(self, icon, stats, xp, position): 61 | self.sprite = icon 62 | self.stats = stats 63 | self.xp = xp 64 | self.position = position 65 | self.calc_max_HP() 66 | self.hp = self.max_hp 67 | 68 | def interact(self, engine, hero): 69 | self.hp -= hero.stats["strength"] 70 | if random.randint(0, 20) < 15: 71 | hero.hp -= self.stats["strength"]*10 72 | hero.exp += self.xp 73 | while hero.exp >= 100 * (2 ** (hero.level - 1)): 74 | engine.notify("level up!") 75 | hero.level += 1 76 | hero.stats["strength"] += 2 77 | hero.stats["endurance"] += 4 78 | hero.calc_max_HP() 79 | hero.hp = hero.max_hp 80 | 81 | engine.notify("Got "+str(self.xp)+" xp") 82 | 83 | if hero.hp <= 0: 84 | engine.notify("You're dead") 85 | engine.game_process = False 86 | 87 | 88 | class Hero(Creature): 89 | 90 | def __init__(self, stats, icon): 91 | pos = [1, 1] 92 | self.level = 1 93 | self.exp = 0 94 | self.gold = 0 95 | self.min_x = self.min_y = 5 96 | super().__init__(icon, stats, pos) 97 | 98 | def draw(self, display): 99 | sprite_size = self.sprite.get_size()[0] 100 | display.blit(self.sprite, [5*sprite_size, 101 | 5*sprite_size]) 102 | 103 | def level_up(self): 104 | while self.exp >= 100 * (2 ** (self.level - 1)): 105 | yield "level up!" 106 | self.level += 1 107 | self.stats["strength"] += 2 108 | self.stats["endurance"] += 2 109 | self.calc_max_HP() 110 | self.hp = self.max_hp 111 | 112 | def exp_interact(self, exp): 113 | self.exp += exp 114 | self.level_up() 115 | 116 | 117 | class Effect(Hero): 118 | 119 | def __init__(self, base): 120 | self.base = base 121 | self.stats = self.base.stats.copy() 122 | self.apply_effect() 123 | self.min_x = self.min_y = 0 124 | 125 | @property 126 | def position(self): 127 | return self.base.position 128 | 129 | @position.setter 130 | def position(self, value): 131 | self.base.position = value 132 | 133 | @property 134 | def level(self): 135 | return self.base.level 136 | 137 | @level.setter 138 | def level(self, value): 139 | self.base.level = value 140 | 141 | @property 142 | def gold(self): 143 | return self.base.gold 144 | 145 | @gold.setter 146 | def gold(self, value): 147 | self.base.gold = value 148 | 149 | @property 150 | def hp(self): 151 | return self.base.hp 152 | 153 | @hp.setter 154 | def hp(self, value): 155 | self.base.hp = value 156 | 157 | @property 158 | def max_hp(self): 159 | return self.base.max_hp 160 | 161 | @max_hp.setter 162 | def max_hp(self, value): 163 | self.base.max_hp = value 164 | 165 | @property 166 | def exp(self): 167 | self.level_up() 168 | self.base.level_up() 169 | return self.base.exp 170 | 171 | @exp.setter 172 | def exp(self, value): 173 | self.level_up() 174 | self.base.level_up() 175 | self.base.exp = value 176 | 177 | @property 178 | def sprite(self): 179 | return self.base.sprite 180 | 181 | @abstractmethod 182 | def apply_effect(self): 183 | pass 184 | 185 | 186 | class Berserk(Effect): 187 | def apply_effect(self): 188 | self.stats["strength"] += 2 189 | super().apply_effect() 190 | 191 | 192 | class Blessing(Effect): 193 | def apply_effect(self): 194 | self.stats["strength"] += 2 195 | self.stats["luck"] += 2 196 | self.stats["intelligence"] += 2 197 | super().apply_effect() 198 | 199 | 200 | class Weakness(Effect): 201 | def apply_effect(self): 202 | self.stats["strength"] -= 2 203 | super().apply_effect() 204 | # My Effect 205 | 206 | 207 | class Curse(Effect): 208 | def apply_effect(self): 209 | self.stats["strength"] -= 2 210 | self.stats["luck"] -= 2 211 | self.stats["intelligence"] -= 2 212 | super().apply_effect() 213 | -------------------------------------------------------------------------------- /week_5/final_project/levels.yml: -------------------------------------------------------------------------------- 1 | levels: 2 | - !empty_map {} 3 | - !special_map 4 | rat: 5 5 | - !special_map 6 | rat: 10 7 | knight: 5 8 | - !special_map 9 | rat: 10 10 | knight: 10 11 | - !random_map {} -------------------------------------------------------------------------------- /week_5/final_project/objects.yml: -------------------------------------------------------------------------------- 1 | objects: 2 | stairs: 3 | sprite: [stair.png] 4 | action: reload_game 5 | min-count: 1 6 | max-count: 1 7 | chest: 8 | sprite: [chest.png] 9 | action: add_gold 10 | min-count: 0 11 | max-count: 3 12 | ally: 13 | bless: 14 | sprite: [NPC_1.png] 15 | action: apply_blessing 16 | min-count: 1 17 | max-count: 6 18 | remove: 19 | sprite: [NPC_2.png] 20 | action: remove_effect 21 | min-count: 1 22 | max-count: 6 23 | heal: 24 | sprite: [NPC_3.png] 25 | action: restore_hp 26 | min-count: 0 27 | max-count: 3 28 | enemies: 29 | rat: 30 | sprite: [rat.png] 31 | strength: 2 32 | endurance: 2 33 | intelligence: 2 34 | luck: 1 35 | experience: 50 36 | knight: 37 | sprite: [Enemy_1.png] 38 | strength: 15 39 | endurance: 15 40 | intelligence: 15 41 | luck: 10 42 | experience: 200 43 | naga: 44 | sprite: [Naga.png] 45 | strength: 25 46 | endurance: 25 47 | intelligence: 25 48 | luck: 10 49 | experience: 500 50 | dragon: 51 | sprite: [dragon.png] 52 | strength: 50 53 | endurance: 50 54 | intelligence: 50 55 | luck: 5 56 | experience: 1000 -------------------------------------------------------------------------------- /week_5/final_project/texture/Ground_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/Ground_1.png -------------------------------------------------------------------------------- /week_5/final_project/texture/Ground_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/Ground_2.png -------------------------------------------------------------------------------- /week_5/final_project/texture/Ground_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/Ground_3.png -------------------------------------------------------------------------------- /week_5/final_project/texture/Hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/Hero.png -------------------------------------------------------------------------------- /week_5/final_project/texture/ally/NPC_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/ally/NPC_1.png -------------------------------------------------------------------------------- /week_5/final_project/texture/ally/NPC_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/ally/NPC_2.png -------------------------------------------------------------------------------- /week_5/final_project/texture/ally/NPC_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/ally/NPC_3.png -------------------------------------------------------------------------------- /week_5/final_project/texture/enemies/Enemy_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/enemies/Enemy_1.png -------------------------------------------------------------------------------- /week_5/final_project/texture/enemies/Naga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/enemies/Naga.png -------------------------------------------------------------------------------- /week_5/final_project/texture/enemies/dragon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/enemies/dragon.png -------------------------------------------------------------------------------- /week_5/final_project/texture/enemies/rat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/enemies/rat.png -------------------------------------------------------------------------------- /week_5/final_project/texture/objects/chest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/objects/chest.png -------------------------------------------------------------------------------- /week_5/final_project/texture/objects/stair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/objects/stair.png -------------------------------------------------------------------------------- /week_5/final_project/texture/wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Searge/mipt_oop/3ecc297559654b29d2afd8a65b106b143a98c35f/week_5/final_project/texture/wall.png -------------------------------------------------------------------------------- /week_5/readme.md: -------------------------------------------------------------------------------- 1 | # Игра «Рыцарь в подземелье» 2 | 3 | В данном задании Вам необходимо будет закончить разработку полноценной ролевой игры «Рыцарь в подземелье». В данной игре необходимо будет играть за рыцаря, который путешествует по многоэтажному подземелью, борется с врагами и собирает сокровища. 4 | 5 | Вам будет дан код движка игры, текстуры и игровая логика. Некоторые из классов и методов изначально не реализованы в предоставленном коде. Их и необходимо будет реализовать в данном задании. В помощь вам будут даны полные диаграммы классов, которые должны быть представлены в проекте. 6 | 7 | Кроме реализации новых методов, необходимо будет дописать недостающий код в некоторые из существующих функций и методов. Места, в которых требуется исправление, помечены меткой `#FIXME`. 8 | 9 | Кроме основных заданий, Вам будут предложены дополнительные задания. В них вам нужно будет немного усовершенствовать игру. При выполнении дополнительных заданий старайтесь быть креативными. 10 | 11 | В качестве ответа вам необходимо будет прикрепить архив, содержащий всю структуру папок Вашего проекта. 12 | 13 | 1. Должны быть реализованы все необходимые классы; 14 | 2. Схема наследования должна соответствовать заданной; 15 | 3. Программа должна корректно выполняться с исходными YAML файлами; 16 | 4. Реализация дополнительных заданий 17 | 18 | ## Objects.py 19 | 20 | ![Objects](../img/Objects.png) 21 | 22 | ## ScreenEngine.py 23 | 24 | ![Screen](../img/ScreenEngine.png) 25 | 26 | ## Service.py 27 | 28 | ![Service](../img/Service.png) 29 | 30 | ### Дополнительные задачи 31 | 32 | 1. Реализованы собственный противник и союзник; 33 | 2. Добавлена отрисовка миникарты; 34 | 3. Реализован дополнительный эффект. 35 | --------------------------------------------------------------------------------