180 lines
80 KiB
Python
180 lines
80 KiB
Python
|
"""
|
|||
|
https://stepik.org/lesson/468999/step/6?unit=459820
|
|||
|
|
|||
|
Рисунок управляется стрелками и меняется
|
|||
|
в зависимости от направления движения.
|
|||
|
|
|||
|
Для выхода нужно нажать Q или Esc
|
|||
|
|
|||
|
Картинки захардкожены в прямо в скрипт
|
|||
|
Зеркальная копия генерируется динамически
|
|||
|
Зависимости: graph pillow
|
|||
|
|
|||
|
Библиотека graph: https://kpolyakov.spb.ru/download/pygraph.zip
|
|||
|
"""
|
|||
|
|
|||
|
import graph
|
|||
|
from graph import *
|
|||
|
import PIL
|
|||
|
import os
|
|||
|
|
|||
|
from typing import NamedTuple
|
|||
|
from contextlib import contextmanager
|
|||
|
import types
|
|||
|
import tempfile
|
|||
|
import base64
|
|||
|
|
|||
|
consts = types.SimpleNamespace()
|
|||
|
consts.VK_Q = 81
|
|||
|
consts.VK_W = 87
|
|||
|
consts.VK_A = 65
|
|||
|
consts.VK_S = 83
|
|||
|
consts.VK_D = 68
|
|||
|
|
|||
|
# объявление переменные для хранения картинки
|
|||
|
hardcoded_image: bytes
|
|||
|
hardcoded_bg: bytes
|
|||
|
|
|||
|
|
|||
|
class Point(NamedTuple):
|
|||
|
"""
|
|||
|
Вспомогательный класс для упрощения работы с координатами (сложение)
|
|||
|
"""
|
|||
|
|
|||
|
x: int | float
|
|||
|
y: int | float
|
|||
|
|
|||
|
def __add__(self, other):
|
|||
|
if isinstance(other, self.__class__):
|
|||
|
return self.__class__(self.x + other.x, self.y + other.y)
|
|||
|
if isinstance(other, (int, float)):
|
|||
|
return self.__class__(self.x + other, self.y + other)
|
|||
|
return NotImplemented
|
|||
|
|
|||
|
def __sub__(self, other):
|
|||
|
if isinstance(other, self.__class__):
|
|||
|
return self.__class__(self.x - other.x, self.y - other.y)
|
|||
|
if isinstance(other, (int, float)):
|
|||
|
return self.__class__(self.x - other, self.y - other)
|
|||
|
return NotImplemented
|
|||
|
|
|||
|
|
|||
|
data = {"delta": Point(0, 0), "right": False, "last_right": False, "callbacks": []}
|
|||
|
|
|||
|
|
|||
|
@contextmanager
|
|||
|
def hardcoded_as_tmp(b64data: bytes, ext: str = ".png"):
|
|||
|
with tempfile.NamedTemporaryFile(
|
|||
|
mode="wb", suffix=ext, delete_on_close=False
|
|||
|
) as fp:
|
|||
|
data = base64.b64decode(b64data)
|
|||
|
fp.write(data)
|
|||
|
fp.close()
|
|||
|
yield fp.name
|
|||
|
|
|||
|
|
|||
|
@contextmanager
|
|||
|
def tmp_flipped_img(src: str):
|
|||
|
"""создание временнрого файла с зеркальной копией картинки"""
|
|||
|
|
|||
|
# получаем оригинальное расширение
|
|||
|
_, ext = os.path.splitext(src)
|
|||
|
if not ext.startswith("."):
|
|||
|
ext = f".{ext}"
|
|||
|
|
|||
|
# открытие
|
|||
|
image_obj = PIL.Image.open(src)
|
|||
|
rotated_image = image_obj.transpose(Image.FLIP_LEFT_RIGHT) # зеркалирование
|
|||
|
with tempfile.NamedTemporaryFile(
|
|||
|
mode="wb", suffix=ext, delete_on_close=False
|
|||
|
) as fp:
|
|||
|
fp.close()
|
|||
|
# сохранение
|
|||
|
rotated_image.save(fp.name)
|
|||
|
yield fp.name
|
|||
|
|
|||
|
|
|||
|
def make_ghost_on_scene(start_pos=None):
|
|||
|
start_pos = start_pos or Point(0, 0)
|
|||
|
|
|||
|
scene_sz = Point(1000, 1000)
|
|||
|
box_sz = Point(200, 150)
|
|||
|
box_offset = Point(400, 425)
|
|||
|
|
|||
|
# рисуем фон
|
|||
|
with hardcoded_as_tmp(hardcoded_bg) as bg_filename:
|
|||
|
image(0, 0, bg_filename)
|
|||
|
|
|||
|
# рисуем картинку
|
|||
|
box_pos = start_pos + box_offset
|
|||
|
with hardcoded_as_tmp(hardcoded_image) as img_filename:
|
|||
|
img_l = PIL.ImageTk.PhotoImage(PIL.Image.open(img_filename))
|
|||
|
obj = image(*box_pos, img_filename)
|
|||
|
# загружаем зеркальную копию
|
|||
|
with tmp_flipped_img(img_filename) as flipped_filename:
|
|||
|
img_r = PIL.ImageTk.PhotoImage(PIL.Image.open(flipped_filename))
|
|||
|
|
|||
|
def update():
|
|||
|
delta = data["delta"]
|
|||
|
coord = Point(xCoord(obj), yCoord(obj)) - start_pos
|
|||
|
# отрабатываем изменение направления
|
|||
|
if data["right"] != data["last_right"]:
|
|||
|
# меняем картинку
|
|||
|
img = img_r if data["right"] else img_l
|
|||
|
changeProperty(obj, image=img)
|
|||
|
data["last_right"] = data["right"]
|
|||
|
if (
|
|||
|
coord.x + delta.x < 0
|
|||
|
or coord.x + box_sz.x + delta.x > scene_sz.x
|
|||
|
or coord.y + delta.y < 0
|
|||
|
or coord.y + box_sz.y + delta.y > scene_sz.y
|
|||
|
):
|
|||
|
return
|
|||
|
moveObjectBy(obj, delta.x, delta.y)
|
|||
|
|
|||
|
data.setdefault("callbacks", []).append(update)
|
|||
|
|
|||
|
|
|||
|
def keyPressed(event):
|
|||
|
match event.keycode:
|
|||
|
case graph.VK_LEFT:
|
|||
|
data["delta"] = Point(-15, 0)
|
|||
|
data["right"] = False
|
|||
|
case graph.VK_RIGHT:
|
|||
|
data["delta"] = Point(15, 0)
|
|||
|
data["right"] = True
|
|||
|
case graph.VK_UP:
|
|||
|
data["delta"] = Point(0, -15)
|
|||
|
case graph.VK_DOWN:
|
|||
|
data["delta"] = Point(0, 15)
|
|||
|
case consts.VK_Q | graph.VK_ESCAPE:
|
|||
|
killTimer(data["update_timer"])
|
|||
|
close()
|
|||
|
|
|||
|
|
|||
|
def update():
|
|||
|
for callback in data["callbacks"]:
|
|||
|
callback()
|
|||
|
data["delta"] = Point(0, 0)
|
|||
|
|
|||
|
|
|||
|
def main():
|
|||
|
canvasSize(1000, 1000)
|
|||
|
make_ghost_on_scene()
|
|||
|
|
|||
|
data["update_timer"] = onTimer(update, 40)
|
|||
|
onKey(keyPressed)
|
|||
|
run()
|
|||
|
|
|||
|
|
|||
|
# image = ghost.png ----
|
|||
|
|
|||
|
hardcoded_image = b"iVBORw0KGgoAAAANSUhEUgAAAMgAAACWCAYAAACb3McZAAAAxHpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjabVDbDcMwCPxnio7AKw4ex2lSqRt0/GKDqyTqST4Dh84YOD7vFzw6mBR0Wa3UUtChVSs3DwwDbTChDh54To2udaglBfaS+C2RWtZp1ulnEFfzaDkZ2TOF7SpUTX+7GeVD0idiD/Y5URoJh0Bp0OJbWKqt5y9sB15hcaCTVdzSLLRbrqtvb1/8HWE+hASdRUr0SD8K0jwQZ0+8kaSMmKKek/hC/u1pAr579VnF7llVHgAAAYNpQ0NQSUNDIHByb2ZpbGUAAHicfZE9SMNAHMVfU6VSKg7tUMQhQ3Wyi4o4lioWwUJpK7TqYHLpFzRpSFJcHAXXgoMfi1UHF2ddHVwFQfADxNnBSdFFSvxfUmgR48FxP97de9y9A4R2nanmQAJQNcvIppJiobgqBl7hRxQhhBGUmKmnc4t5eI6ve/j4ehfnWd7n/hzDSslkgE8kTjDdsIg3iGc3LZ3zPnGEVSWF+Jx40qALEj9yXXb5jXPFYYFnRox8dp44QixW+ljuY1Y1VOIZ4piiapQvFFxWOG9xVutN1r0nf2GopK3kuE5zDCksIY0MRMhoooY6LMRp1UgxkaX9pId/1PFnyCWTqwZGjgU0oEJy/OB/8Ltbszw95SaFksDgi21/jAOBXaDTsu3vY9vunAD+Z+BK6/kbbWDuk/RWT4sdASPbwMV1T5P3gMsdIPqkS4bkSH6aQrkMvJ/RNxWB8C0QXHN76+7j9AHIU1fLN8DBITBRoex1j3cP9ff275lufz9n7nKizH3LfgAADXZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgIHhtbG5zOkdJTVA9Imh0dHA6Ly93d3cuZ2ltcC5vcmcveG1wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICB4bXBNTTpEb2N1bWVudElEPSJnaW1wOmRvY2lkOmdpbXA6MGJlMmZlODEtMWE2MS00NGM4LWJmYWYtNTNkZmE5YWEwNzRhIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjUwZWNmYjJjLWQ4NjEtNDYwZC1hYTFiLWExYmZmZWJiMmUxYiIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjdiYzE3MDQyLTFlYWItNDFmMS04Y2Q4LTE3ZDU5M2VjNzhkMSIKICAgZGM6Rm9ybWF0PSJpbWFnZS9wbmciCiAgIEdJTVA6QVBJPSIyLjAiCiAgIEdJTVA6UGxhdGZvcm09IldpbmRvd3MiCiAgIEdJTVA6VGltZVN0YW1wPSIxNzExMjg2NDY2MzgxMTE2IgogICBHSU1QOlZlcnNpb249IjIuMTAuMzYiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHhtcDpDcmVhdG9yVG9vbD0iR0lNUCAyLjEwIgogICB4bXA6TWV0YWRhdGFEYXRlPSIyMDI0OjAzOjI0VDE2OjIxOjAzKzAzOjAwIgogICB4bXA6TW9kaWZ5RGF0ZT0iMjAyNDowMzoyNFQxNjoyMTowMyswMzowMCI+CiAgIDx4bXBNTTpIaXN0b3J5PgogICAgPHJkZjpTZXE+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjEwZWFkMTc3LWE3OTItNDQxMC1iMmNmLTY4MThlZGQxZjYwMSIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iR2ltcCAyLjEwIChXaW5kb3dzKSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wMy0yNFQxNjoyMTowNiIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICA
|
|||
|
|
|||
|
|
|||
|
# background=bg1k.png ----
|
|||
|
|
|||
|
hardcoded_bg = b"iVBORw0KGgoAAAANSUhEUgAAA+gAAAPoCAYAAABNo9TkAAAAxnpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjabVDbDcMgDPxnio7gF2DGIQ2VukHHr8FOlUQ9Cft8JhfjND7vV3pMEEqSXLW0UsAgTRp1IwqOviKCrLgwFDjUi55+DTJpMq9Ai2c89PjgyNiN5ZORPqOxXRtNPJPejMgTz4km38OohRGTNzAMuj8LStN6fsI24Ar1k2agiusah/u9lmrb27P9h4kGI4NF5uID8DySuBthi1bYReRsXLguvcUktpB/ezqQvsFPWfsXnLp+AAABg2lDQ1BJQ0MgcHJvZmlsZQAAeJx9kT1Iw0AcxV9TpVIqDu1QxCFDdbKLijiWKhbBQmkrtOpgcukXNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APE2cFJ0UVK/F9SaBHjwXE/3t173L0DhHadqeZAAlA1y8imkmKhuCoGXuFHFCGEEZSYqadzi3l4jq97+Ph6F+dZ3uf+HMNKyWSATyROMN2wiDeIZzctnfM+cYRVJYX4nHjSoAsSP3JddvmNc8VhgWdGjHx2njhCLFb6WO5jVjVU4hnimKJqlC8UXFY4b3FW603WvSd/YaikreS4TnMMKSwhjQxEyGiihjosxGnVSDGRpf2kh3/U8WfIJZOrBkaOBTSgQnL84H/wu1uzPD3lJoWSwOCLbX+MA4FdoNOy7e9j2+6cAP5n4Err+RttYO6T9FZPix0BI9vAxXVPk/eAyx0g+qRLhuRIfppCuQy8n9E3FYHwLRBcc3vr7uP0AchTV8s3wMEhMFGh7HWPdw/19/bvmW5/P2fucqLMfct+AAANdmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6R0lNUD0iaHR0cDovL3d3dy5naW1wLm9yZy94bXAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgIHhtcE1NOkRvY3VtZW50SUQ9ImdpbXA6ZG9jaWQ6Z2ltcDoxOGI1MGY0MS04YzFkLTRiYWQtYmQ4OS1jMjViMzQ1NzQ0ZjAiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Yjg1Yjg0MDAtMWNiZC00ODhjLWJlNzctODEwN2Y3MzdmMGVhIgogICB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NjU3MTRlMGQtMjBmMC00ZWIyLWEzZDItNmE3N2VkMDEyMDAwIgogICBkYzpGb3JtYXQ9ImltYWdlL3BuZyIKICAgR0lNUDpBUEk9IjIuMCIKICAgR0lNUDpQbGF0Zm9ybT0iV2luZG93cyIKICAgR0lNUDpUaW1lU3RhbXA9IjE3MTEyODQ0MzY3MzkxMjYiCiAgIEdJTVA6VmVyc2lvbj0iMi4xMC4zNiIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQ6MDM6MjRUMTU6NDc6MDkrMDM6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDI0OjAzOjI0VDE1OjQ3OjA5KzAzOjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MzRlYmZhMGUtNWYwZC00N2ZkLTk5NjYtODVjZjU0MTM4YzI0IgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKFdpbmRvd3MpIgogICAgICBzdEV2dDp3aGVuPSIyMDI0LTAzLTI0VDE1OjQ3OjE2Ii8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgIC
|
|||
|
|
|||
|
main()
|