├── 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 | 
11 | 
12 | 
13 | 
14 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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''
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'
'
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 |
10 |
11 |
--------------------------------------------------------------------------------
/week_5/final_project/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 | 
21 |
22 | ## ScreenEngine.py
23 |
24 | 
25 |
26 | ## Service.py
27 |
28 | 
29 |
30 | ### Дополнительные задачи
31 |
32 | 1. Реализованы собственный противник и союзник;
33 | 2. Добавлена отрисовка миникарты;
34 | 3. Реализован дополнительный эффект.
35 |
--------------------------------------------------------------------------------