Skip to content

Commit 60078fe

Browse files
committed
Added Industrial_developed_hangman
1 parent 96d0eb6 commit 60078fe

File tree

8 files changed

+1514
-0
lines changed

8 files changed

+1514
-0
lines changed

Industrial_developed_hangman/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
lint:
2+
poetry run isort src tests
3+
poetry run flake8 src tests
4+
poetry run mypy src
5+
poetry run mypy tests
6+
7+
test:
8+
poetry run pytest
9+
10+
build:
11+
python src/hangman/main.py
12+
install:
13+
pip install poetry
14+
poetry install
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
This is a simple game hangman
2+
3+
to install dependencies got to repository "Industrial_developed_hangman" by `cd .\Industrial_developed_hangman\` and run `make install`
4+
5+
to start it use `make build` command
6+
7+
also makefile have lint command to lint source code

Industrial_developed_hangman/poetry.lock

Lines changed: 1235 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[tool.poetry]
2+
name = "Hangman"
3+
version = "0.2.0"
4+
description = ""
5+
authors = ["DiodDan <[email protected]>"]
6+
readme = "README.md"
7+
packages = [{include = "hangman", from = "src"}]
8+
9+
[tool.poetry.dependencies]
10+
python = "^3.9"
11+
requests = "2.31.0"
12+
colorama = "0.4.6"
13+
beautifulsoup4 = "4.12"
14+
15+
16+
[tool.poetry.group.dev.dependencies]
17+
mypy = "1.5.1"
18+
wemake-python-styleguide = "0.18.0"
19+
isort = "5.12.0"
20+
pytest = "7.4.2"
21+
pytest-cov = "4.1.0"
22+
pytest-timeout = "2.2.0"
23+
pytest-randomly = "3.15.0"
24+
requests-mock = "1.11.0"
25+
pytest-freezer = "0.4.8"
26+
types-requests = " 2.31.0.2"
27+
28+
[build-system]
29+
requires = ["poetry-core", "colorama", "bs4", "requests"]
30+
build-backend = "poetry.core.masonry.api"
31+
32+
[tool.isort]
33+
line_length = 80
34+
multi_line_output = 3
35+
include_trailing_comma = true
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[pytest]
2+
markers =
3+
internet_required: marks tests that requires internet connection (deselect with '-m "not internet_required"')
4+
serial
5+
timeout = 20
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
[flake8]
2+
max-line-length = 120
3+
docstring_style = sphinx
4+
max-arguments = 6
5+
exps-for-one-empty-line = 0
6+
ignore =
7+
D100,
8+
D104,
9+
10+
per-file-ignores =
11+
tests/*:
12+
# Missing docstring in public class
13+
D101,
14+
# Missing docstring in public method
15+
D102,
16+
# Missing docstring in public function
17+
D103,
18+
# Missing docstring in magic method
19+
D105,
20+
# Missing docstring in __init__
21+
D107,
22+
# Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
23+
S101,
24+
# Found magic number
25+
WPS432,
26+
# Found wrong keyword: pass
27+
WPS420,
28+
# Found incorrect node inside `class` body
29+
WPS604,
30+
# Found outer scope names shadowing: message_update
31+
WPS442,
32+
# Found comparison with float or complex number
33+
WPS459,
34+
# split between test action and assert
35+
WPS473,
36+
# Found compare with falsy constant
37+
WPS520,
38+
# Found string literal over-use
39+
WPS226
40+
# Found overused expression
41+
WPS204
42+
# Found too many module members
43+
WPS202
44+
45+
[mypy]
46+
ignore_missing_imports = True
47+
check_untyped_defs = True
48+
disallow_untyped_calls = True

Industrial_developed_hangman/src/hangman/__init__.py

Whitespace-only changes.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import random
2+
import time
3+
from enum import Enum
4+
from pathlib import Path
5+
from typing import Callable, List
6+
7+
import requests
8+
from bs4 import BeautifulSoup
9+
from colorama import Fore, Style
10+
11+
DEBUG = False
12+
success_code = 200
13+
request_timeout = 1000
14+
data_path = Path(__file__).parent.parent.parent / 'Data'
15+
year = 4800566455
16+
17+
18+
class Source(Enum):
19+
"""Enum that represents switch between local and web word parsing."""
20+
21+
FROM_FILE = 0 # noqa: WPS115
22+
FROM_INTERNET = 1 # noqa: WPS115
23+
24+
25+
def print_wrong(text: str, print_function: Callable[[str], None]) -> None:
26+
"""
27+
Print styled text(red).
28+
29+
:parameter text: text to print.
30+
:parameter print_function: Function that will be used to print in game.
31+
"""
32+
text_to_print = Style.RESET_ALL + Fore.RED + text
33+
print_function(text_to_print)
34+
35+
36+
def print_right(text: str, print_function: Callable[[str], None]) -> None:
37+
"""
38+
Print styled text(red).
39+
40+
:parameter text: text to print.
41+
:parameter print_function: Function that will be used to print in game.
42+
"""
43+
print_function(Style.RESET_ALL + Fore.GREEN + text)
44+
45+
46+
def parse_word_from_local(choice_function: Callable[[List[str]], str] = random.choice) -> str:
47+
# noqa: DAR201
48+
"""
49+
Parse word from local file.
50+
51+
:parameter choice_function: Function that will be used to choice a word from file.
52+
:returns str: string that contains the word.
53+
:raises FileNotFoundError: file to read words not found.
54+
"""
55+
try:
56+
with open(data_path / 'local_words.txt', encoding='utf8') as words_file:
57+
return choice_function(words_file.read().split('\n'))
58+
except FileNotFoundError:
59+
raise FileNotFoundError('File local_words.txt was not found')
60+
61+
62+
def parse_word_from_site(url: str = 'https://randomword.com') -> str:
63+
# noqa: DAR201
64+
"""
65+
Parse word from website.
66+
67+
:param url: url that word will be parsed from.
68+
:return Optional[str]: string that contains the word.
69+
:raises ConnectionError: no connection to the internet.
70+
:raises RuntimeError: something go wrong with getting the word from site.
71+
"""
72+
try:
73+
page: requests.Response = requests.get(url, timeout=request_timeout)
74+
except ConnectionError:
75+
raise ConnectionError('There is no connection to the internet')
76+
if page.status_code == success_code:
77+
soup = BeautifulSoup(page.text, 'html.parser')
78+
return soup.find('div', id='random_word').text
79+
raise RuntimeError('Something go wrong with getting the word from site')
80+
81+
82+
class MainProcess(object):
83+
"""Manages game process."""
84+
85+
def __init__(self, source: Enum, pr_func: Callable, in_func: Callable, ch_func: Callable) -> None:
86+
"""
87+
Init MainProcess object.
88+
89+
:parameter in_func: Function that will be used to get input in game.
90+
:parameter source: Represents source to get word.
91+
:parameter pr_func: Function that will be used to print in game.
92+
:parameter ch_func: Function that will be used to choice word.
93+
"""
94+
self._source = source
95+
self._answer_word = ''
96+
self._word_string_to_show = ''
97+
self._guess_attempts_coefficient = 2
98+
self._print_function = pr_func
99+
self._input_function = in_func
100+
self._choice_function = ch_func
101+
102+
def get_word(self) -> str:
103+
# noqa: DAR201
104+
"""
105+
Parse word(wrapper for local and web parse).
106+
107+
:returns str: string that contains the word.
108+
:raises AttributeError: Not existing enum
109+
"""
110+
if self._source == Source.FROM_INTERNET:
111+
return parse_word_from_site()
112+
elif self._source == Source.FROM_FILE:
113+
return parse_word_from_local(self._choice_function)
114+
raise AttributeError('Non existing enum')
115+
116+
def user_lose(self) -> None:
117+
"""Print text for end of game and exits."""
118+
print_wrong(f"YOU LOST(the word was '{self._answer_word}')", self._print_function) # noqa:WPS305
119+
120+
def user_win(self) -> None:
121+
"""Print text for end of game and exits."""
122+
print_wrong(f'{self._word_string_to_show} YOU WON', self._print_function) # noqa:WPS305
123+
124+
def game_process(self, user_character: str) -> bool:
125+
# noqa: DAR201
126+
"""
127+
Process user input.
128+
129+
:parameter user_character: User character.
130+
:returns bool: state of game.
131+
"""
132+
if user_character in self._answer_word:
133+
word_list_to_show = list(self._word_string_to_show)
134+
for index, character in enumerate(self._answer_word):
135+
if character == user_character:
136+
word_list_to_show[index] = user_character
137+
self._word_string_to_show = ''.join(word_list_to_show)
138+
else:
139+
print_wrong('There is no such character in word', self._print_function)
140+
if self._answer_word == self._word_string_to_show:
141+
self.user_win()
142+
return True
143+
return False
144+
145+
def start_game(self) -> None:
146+
"""Start main process of the game."""
147+
if time.time() > year:
148+
print_right('this program is more then 100years age', self._print_function)
149+
with open(data_path / 'text_images.txt', encoding='utf8') as text_images_file:
150+
print_wrong(text_images_file.read(), self._print_function)
151+
print_wrong('Start guessing...', self._print_function)
152+
self._answer_word = self.get_word()
153+
self._word_string_to_show = '_' * len(self._answer_word)
154+
attempts_amount = int(self._guess_attempts_coefficient * len(self._answer_word))
155+
if DEBUG:
156+
print_right(self._answer_word, self._print_function)
157+
for attempts in range(attempts_amount):
158+
user_remaining_attempts = attempts_amount - attempts
159+
print_right(f'You have {user_remaining_attempts} more attempts', self._print_function) # noqa:WPS305
160+
print_right(f'{self._word_string_to_show} enter character to guess: ', self._print_function) # noqa:WPS305
161+
user_character = self._input_function().lower()
162+
if self.game_process(user_character):
163+
break
164+
if '_' in self._word_string_to_show:
165+
self.user_lose()
166+
167+
168+
if __name__ == '__main__':
169+
main_process = MainProcess(Source(1), print, input, random.choice)
170+
main_process.start_game()

0 commit comments

Comments
 (0)