feat(ais): update lab#2

This commit is contained in:
maxbarsukov 2024-10-06 20:32:50 +03:00
parent e3273f0c43
commit 522f0754bb
21 changed files with 719 additions and 3 deletions

View File

@ -0,0 +1,134 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Machine codes
*.out
# debug info
dbg/
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Ruff
.ruff_cache
## pyenv
# poetry.lock
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Translations
*.mo
*.pot
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# PEP 582
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# IDEs
.idea/
.vscode/
# Draw.io schemas
*.drawio
# Misc
*.DS_Store

View File

@ -8,5 +8,6 @@ MD031: false
MD032: false
MD033: false
MD040: false
MD046: false
MD049: false
MD051: false # Link fragments should be valid

View File

@ -19,6 +19,7 @@
Мне 13 лет, мне нравятся: RPG, инди-игры
### Нужно
Спарсить строку, разбить на факты, построить запрос, используя эти предикаты (**формат входной строки фиксированный**, искать частичное соответствие подстроки не нужно)
@ -32,6 +33,54 @@
---
## Как запустить?
*Убедитесь, что у вас установлены [Python 3.12](https://www.python.org/downloads/release/python-3123/) и [Poetry](https://python-poetry.org/)*
- Сначала:
```sh
poetry install
```
- Линтер и форматтер:
```sh
make
# или
poetry run ruff format .
poetry run ruff check .
```
- Запуск программы:
```sh
make run
# или
poetry run main
```
### Возможные запросы
Примеры запросов, которые вам доступны:
- Какие страны входят в альянс Ось?
- Какие страны воюют друг с другом?
- Какие страны являются коммунистическими?
- Какие страны могут вступить в альянс с uk?
- Может ли usa создать новый альянс при текущем уровне мировой напряженности?
- Какой количество рекрутов в poland?
- Какие страны имеют выход к Чёрному морю?
- Армии каких стран имеют в своём составе пехотные дивизии?
- Какие страны находятся в оборонительной войне против germany?
- Какое население провинции ulaanbaatar в mongolia?
- Какие страны марионетки japan?
- Может ли ussr пересечь Босфор и Дарданеллы?
- Я играю за страну uk. Какое мое текущее отношение к france?
- Может ли hungary сфабриковать цель войны на yugoslavia?
---
## Полезные ссылки
| Ссылка | Описание |

View File

@ -1,5 +1,31 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "ruff"
version = "0.1.15"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"},
{file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"},
{file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"},
{file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"},
{file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"},
{file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"},
{file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"},
{file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"},
{file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"},
{file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"},
{file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"},
{file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"},
{file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"},
{file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"},
{file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"},
{file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"},
{file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"},
]
[[package]]
name = "swiplserver"
version = "1.0.2"
@ -14,4 +40,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "ebea2b4b9934dd39850ad9c2e0e9147431fd8587bc138ff2c44035ac75440e52"
content-hash = "e813a8061617c5c7825a6f86dd5a31a709675bed68251bd1fd010926f49ac0bb"

View File

@ -79,14 +79,13 @@ ignore = [
"E501", # allow lines longer than 80 symbols (up to 120)
"T201", # `print` found
"PD011", # Breaks .values
"RUF001", # Breaks cyrillic strings
"RUF002", # Docstring contains ambiguous `В` (CYRILLIC CAPITAL LETTER VE). Did you mean `B` (LATIN CAPITAL LETTER B)
"RUF003", # Comment contains ambiguous `с` (CYRILLIC SMALL LETTER ES). Did you mean `c` (LATIN SMALL LETTER C)
"PTH123", # `open()` should be replaced by `Path.open()`
# to simplify code for non-Python programmers
"PTH118", # `os.path.join()` should be replaced by `Path` with `/` operator
"UP032", # [*] Use f-string instead of `format` call
]
unfixable = [
"ERA001", # prohibit to "auto-fix" (=remove) commented-out code

View File

@ -0,0 +1,101 @@
import logging
import re
from src.methods import (
can_fabricate_war_goal,
can_form_alliance,
can_go_throw_strait,
find_countries_at_war,
find_countries_by_ideology,
find_countries_in_alliance,
find_countries_near_sea,
find_defensive_countries,
get_countries_by_division,
get_population,
get_possible_allied_countries,
get_puppets,
get_recruits_number,
get_relationship,
)
from swiplserver import PrologMQI, create_posix_path
KNOWLEDGE_BASE_PATH = "./src/prolog/knowledge_base.pl"
incorrect_request = "Неправильный запрос"
requests = [
# Ось / Союзники / Коминтерн / Второй объединённый фронт / Великая восточноазиатская сфера сопроцветания
"Какие страны входят в альянс Ось?",
"Какие страны воюют друг с другом?",
# коммунистическими / демократическими / нейтральными / фашистскими
"Какие страны являются коммунистическими?",
"Какие страны могут вступить в альянс с uk?",
"Может ли usa создать новый альянс при текущем уровне мировой напряженности?",
"Какой количество рекрутов в poland?",
# Чёрному морю / Средиземному морю / Балтийскому морю / Адриатическому морю / Красному морю
# Атлантическому океану / Тихому океану / Индийскому океану
"Какие страны имеют выход к Чёрному морю?",
# пехотные / десантные / артиллерийские / танковые
"Армии каких стран имеют в своём составе пехотные дивизии?",
"Какие страны находятся в оборонительной войне против germany?",
"Какое население провинции ulaanbaatar в mongolia?",
"Какие страны марионетки japan?",
# Босфор и Дарданеллы / Панамский канал / Суэцкий канал / пролив Отранто
# Гибралтарский пролив / Кильский канал / Баб-эль-Мандебский пролив / Сингапурский пролив
"Может ли ussr пересечь Босфор и Дарданеллы?",
"Я играю за страну uk. Какое мое текущее отношение к france?",
"Может ли hungary сфабриковать цель войны на yugoslavia?",
]
patterns = {
r"Какие страны входят в альянс (.+)\?": find_countries_in_alliance.FindCountriesInAlliance,
r"Какие страны воюют друг с другом\?": find_countries_at_war.FindCountriesAtWar,
r"Какие страны являются (.+)\?": find_countries_by_ideology.FindCountriesByIdeology,
r"Какие страны могут вступить в альянс с (.+)\?": get_possible_allied_countries.GetPossibleAlliedCountries,
r"Может ли (.+) создать новый альянс при текущем уровне мировой напряженности\?": can_form_alliance.CanFormAlliance,
r"Какой количество рекрутов в (.+)\?": get_recruits_number.GetRecruitsNumber,
r"Какие страны имеют выход к (.+)\?": find_countries_near_sea.FindCountriesNearSea,
r"Армии каких стран имеют в своём составе (.+) дивизии\?": get_countries_by_division.GetCountriesByDivision,
r"Какие страны находятся в оборонительной войне против (.+)\?": find_defensive_countries.FindDefensiveCountries,
r"Какое население провинции (.+) в (.+)\?": get_population.GetPopulation,
r"Какие страны марионетки (.+)\?": get_puppets.GetPuppets,
r"Может ли (.+) пересечь (.+)\?": can_go_throw_strait.CanGoThrowStrait,
r"Я играю за страну (.+). Какое мое текущее отношение к (.+)\?": get_relationship.GetRelationship,
r"Может ли (.+) сфабриковать цель войны на (.+)\?": can_fabricate_war_goal.CanFabricateWarGoal,
}
def start() -> None:
logging.getLogger().setLevel(logging.WARNING)
with PrologMQI() as mqi:
with mqi.create_thread() as prolog:
path = create_posix_path(KNOWLEDGE_BASE_PATH)
logging.debug("DEBUG: knowledge base path %s", path)
prolog.query(f'consult("{path}")')
logging.debug("DEBUG: knowledge base loaded")
print("\nПримеры запросов, которые вам доступны:", "\n * " + ("\n * ".join(requests)))
print("\nДля завершения введите - exit.")
while True:
query = input("> ")
if query.lower() == "exit":
break
for pattern in patterns:
match = re.match(pattern, query, re.IGNORECASE)
if match is None:
continue
logging.debug("DEBUG: '%s' matches to '%s'", query, match)
processor = patterns[pattern](*match.groups())
processor.run(prolog)
break
else:
print(incorrect_request)
if __name__ == "__main__":
start()

View File

@ -0,0 +1,29 @@
from swiplserver import PrologThread
class CanFabricateWarGoal:
def __init__(self, country1: str, country2: str):
self.country1 = country1
self.country2 = country2
def run(self, prolog: PrologThread):
if self.country1 == self.country2:
print(
f"Нет, {self.country1} не может сфабриковать цель войны на {self.country2}, так как это одна и та же страна."
)
return
res = prolog.query(self.query())
if not res:
self.failure()
else:
self.success()
def query(self):
return f"can_justify_war_goal({self.country1}, {self.country2})."
def success(self):
print(f"Да, {self.country1} может сфабриковать цель войны на {self.country2}.")
def failure(self):
print(f"Нет, {self.country1} не может сфабриковать цель войны на {self.country2}.")

View File

@ -0,0 +1,22 @@
from swiplserver import PrologThread
class CanFormAlliance:
def __init__(self, country: str):
self.country = country
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res:
self.failure()
else:
self.success()
def query(self):
return f"can_form_alliance({self.country})."
def success(self):
print(f"Да, {self.country} может создать новый альянс при текущем уровне мировой напряженности.")
def failure(self):
print(f"Нет, {self.country} не может создать новый альянс при текущем уровне мировой напряженности.")

View File

@ -0,0 +1,38 @@
from swiplserver import PrologThread
class CanGoThrowStrait:
def __init__(self, country: str, strait: str):
self.country = country
self.strait = strait
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res:
self.failure()
else:
self.success()
def query(self):
return f"can_cross_the_strait({self.country}, {self.get_strait()})."
def success(self):
print(f"Да, {self.country} может пересечь {self.strait}.")
def failure(self):
print(f"Нет, {self.country} не может пересечь {self.strait}.")
def get_strait(self):
straits = {
"Босфор и Дарданеллы": "bosporus_and_dardanelles",
"Панамский канал": "panama_canal_strait",
"Суэцкий канал": "suez_canal",
"пролив Отранто": "otranto",
"Гибралтарский пролив": "gibraltar_strait",
"Кильский канал": "kiel_canal",
"Баб-эль-Мандебский пролив": "bab_el_mandeb",
"Сингапурский пролив": "singapur_strait",
}
if self.strait not in straits:
return "not_found"
return straits[self.strait]

View File

@ -0,0 +1,21 @@
from swiplserver import PrologThread
class FindCountriesAtWar:
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res or len(res) == 0:
self.failure(res)
else:
self.success(res)
def query(self):
return "at_offencive_war(Country1, Country2)"
def success(self, res):
print(f"Найдено {len(res)} стран, которые воюют друг с другом:")
for index, line in enumerate(res, 1):
print(f"{index}. {line['Country1']} воюет с {line['Country2']}")
def failure(self, res):
print("Нет стран, которые воюют друг с другом.")

View File

@ -0,0 +1,35 @@
from swiplserver import PrologThread
class FindCountriesByIdeology:
def __init__(self, ideology: str):
self.ideology = ideology
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res or len(res) == 0:
self.failure(res)
else:
self.success(res)
def query(self):
return f"ideology(Country, {self.get_ideology()})."
def success(self, res):
print(f"Найдено {len(res)} стран с идеологией {self.ideology}:")
for index, line in enumerate(res, 1):
print(f"{index}. {line['Country']}")
def failure(self, res):
print(f"Нет стран с идеологией {self.ideology}.")
def get_ideology(self):
ideologies = {
"коммунистическими": "communist",
"демократическими": "democratic",
"нейтральными": "neutral",
"фашистскими": "fascist",
}
if self.ideology not in ideologies:
return "not_found"
return ideologies[self.ideology]

View File

@ -0,0 +1,36 @@
from swiplserver import PrologThread
class FindCountriesInAlliance:
def __init__(self, alliance: str):
self.alliance = alliance
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res or len(res) == 0:
self.failure(res)
else:
self.success(res)
def query(self):
return f"alliance(Country, {self.get_alliance_tag()})"
def success(self, res):
print(f"Найдено {len(res)} стран, которые входят в альянс {self.alliance}:")
for index, line in enumerate(res, 1):
print(f"{index}. ", line["Country"])
def failure(self, res):
print(f"Нет стран, которые входят в альянс {self.alliance}.")
def get_alliance_tag(self):
alliances = {
"Ось": "axis",
"Союзники": "allies",
"Коминтерн": "comintern",
"Второй объединённый фронт": "united_chinese_front",
"Великая восточноазиатская сфера сопроцветания": "great_east_asia_coprosperity_sphere",
}
if self.alliance not in alliances:
return "not_found"
return alliances[self.alliance]

View File

@ -0,0 +1,40 @@
from swiplserver import PrologThread
class FindCountriesNearSea:
def __init__(self, sea: str):
self.sea = sea
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res or len(res) == 0:
self.failure(res)
else:
self.success(res)
def query(self):
return f"has_access_to_sea(Province, {self.get_seas()}), province_of(Country, Province)."
def success(self, res):
unique_res = list({object_["Country"]: object_ for object_ in res}.values())
print(f"Найдено {len(unique_res)} стран с выходом к {self.sea}:")
for index, line in enumerate(unique_res, 1):
print(f"{index}. {line['Country']}")
def failure(self, res):
print(f"Нет стран с выходом к {self.sea}.")
def get_seas(self):
seas = {
"Чёрному морю": "black_sea",
"Средиземному морю": "mediterranean_sea",
"Балтийскому морю": "baltic_sea",
"Адриатическому морю": "adriatic_sea",
"Красному морю": "red_sea",
"Атлантическому океану": "atlantic_ocean",
"Тихому океану": "pacific_ocean",
"Индийскому океану": "indian_ocean",
}
if self.sea not in seas:
return "not_found"
return seas[self.sea]

View File

@ -0,0 +1,24 @@
from swiplserver import PrologThread
class FindDefensiveCountries:
def __init__(self, country: str):
self.country = country
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res or len(res) == 0:
self.failure()
else:
self.success(res)
def query(self):
return f"at_defensive_war(Country, {self.country})."
def success(self, res):
print(f"На {self.country} нападают страны:")
for index, line in enumerate(res, 1):
print(f"{index}. {line['Country']}")
def failure(self):
print(f"{self.country} не находится в оборонительной войне.")

View File

@ -0,0 +1,36 @@
from swiplserver import PrologThread
class GetCountriesByDivision:
def __init__(self, division: str):
self.division = division
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res or len(res) == 0:
self.failure()
else:
self.success(res)
def query(self):
return f"has_division(Army, {self.division_name()}), army(Army, Country)."
def success(self, res):
print(f"Страны, у которых в войсках есть {self.division} дивизии:")
unique_res = list({object_["Country"]: object_ for object_ in res}.values())
for index, line in enumerate(unique_res, 1):
print(f"{index}. {line['Country']}")
def failure(self):
print(f"Нет стран с дивизией {self.division}.")
def division_name(self):
divisions = {
"пехотные": "infantry_division",
"десантные": "naval_division",
"артиллерийские": "artillery_division",
"танковые": "armored_division",
}
if self.division not in divisions:
return "not_found"
return divisions[self.division]

View File

@ -0,0 +1,24 @@
from swiplserver import PrologThread
class GetPopulation:
def __init__(self, province: str, country: str):
self.province = province
self.country = country
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res:
self.failure(res)
else:
self.success(res)
def query(self):
return f"population({self.province}, Population), province_of({self.country}, {self.province})."
def success(self, res):
population = res[0]["Population"]
print(f"Население провинции {self.province} в стране {self.country}: {population}")
def failure(self, res):
print(f"Не удалось найти информацию о населении провинции {self.province} в стране {self.country}.")

View File

@ -0,0 +1,24 @@
from swiplserver import PrologThread
class GetPossibleAlliedCountries:
def __init__(self, country: str):
self.country = country
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res or len(res) == 0:
self.failure()
else:
self.success(res)
def query(self):
return f"can_invite_to_alliance({self.country}, _, Country)."
def success(self, res):
print(f"Страны, которые могут вступить в альянс с {self.country}:")
for index, line in enumerate(res, 1):
print(f"{index}. {line['Country']}")
def failure(self):
print(f"Нет стран, которые могут вступить в альянс с {self.country}.")

View File

@ -0,0 +1,24 @@
from swiplserver import PrologThread
class GetPuppets:
def __init__(self, country: str):
self.country = country
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res or len(res) == 0:
self.failure()
else:
self.success(res)
def query(self):
return f"puppet({self.country}, PuppetCountry)."
def success(self, res):
print(f"Марионетки {self.country}:")
for index, line in enumerate(res, 1):
print(f"{index}. {line['PuppetCountry']}")
def failure(self):
print(f"У {self.country} нет марионеток.")

View File

@ -0,0 +1,23 @@
from swiplserver import PrologThread
class GetRecruitsNumber:
def __init__(self, country: str):
self.country = country
def run(self, prolog: PrologThread):
res = prolog.query(self.query())
if not res:
self.failure(res)
else:
self.success(res)
def query(self):
return f"recruits({self.country}, Recruits)."
def success(self, res):
recruits = res[0]["Recruits"]
print(f"Количество рекрутов в {self.country}: {recruits}")
def failure(self, res):
print(f"Не удалось найти информацию о рекрутах в {self.country}.")

View File

@ -0,0 +1,30 @@
from swiplserver import PrologThread
class GetRelationship:
def __init__(self, country1: str, country2: str):
self.country1 = country1
self.country2 = country2
def run(self, prolog: PrologThread):
if self.country1 == self.country2:
print(
f"Не удалось найти информацию об отношении {self.country1} к {self.country2}, так как это одна и та же страна."
)
return
res = prolog.query(self.query())
if not res:
self.failure()
else:
self.success(res)
def query(self):
return f"current_relationship({self.country1}, {self.country2}, Relationship)."
def success(self, res):
relationship = res[0]["Relationship"]
print(f"Отношение {self.country1} к {self.country2} составляет {relationship}.")
def failure(self):
print(f"Не удалось найти информацию об отношении {self.country1} к {self.country2}.")