Skip to content

Commit

Permalink
Added Industrial_developed_hangman
Browse files Browse the repository at this point in the history
  • Loading branch information
DiodDan committed Nov 1, 2023
1 parent 96d0eb6 commit 60078fe
Show file tree
Hide file tree
Showing 8 changed files with 1,514 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Industrial_developed_hangman/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
lint:
poetry run isort src tests
poetry run flake8 src tests
poetry run mypy src
poetry run mypy tests

test:
poetry run pytest

build:
python src/hangman/main.py
install:
pip install poetry
poetry install
7 changes: 7 additions & 0 deletions Industrial_developed_hangman/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This is a simple game hangman

to install dependencies got to repository "Industrial_developed_hangman" by `cd .\Industrial_developed_hangman\` and run `make install`

to start it use `make build` command

also makefile have lint command to lint source code
1,235 changes: 1,235 additions & 0 deletions Industrial_developed_hangman/poetry.lock

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions Industrial_developed_hangman/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[tool.poetry]
name = "Hangman"
version = "0.2.0"
description = ""
authors = ["DiodDan <[email protected]>"]
readme = "README.md"
packages = [{include = "hangman", from = "src"}]

[tool.poetry.dependencies]
python = "^3.9"
requests = "2.31.0"
colorama = "0.4.6"
beautifulsoup4 = "4.12"


[tool.poetry.group.dev.dependencies]
mypy = "1.5.1"
wemake-python-styleguide = "0.18.0"
isort = "5.12.0"
pytest = "7.4.2"
pytest-cov = "4.1.0"
pytest-timeout = "2.2.0"
pytest-randomly = "3.15.0"
requests-mock = "1.11.0"
pytest-freezer = "0.4.8"
types-requests = " 2.31.0.2"

[build-system]
requires = ["poetry-core", "colorama", "bs4", "requests"]
build-backend = "poetry.core.masonry.api"

[tool.isort]
line_length = 80
multi_line_output = 3
include_trailing_comma = true
5 changes: 5 additions & 0 deletions Industrial_developed_hangman/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[pytest]
markers =
internet_required: marks tests that requires internet connection (deselect with '-m "not internet_required"')
serial
timeout = 20
48 changes: 48 additions & 0 deletions Industrial_developed_hangman/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[flake8]
max-line-length = 120
docstring_style = sphinx
max-arguments = 6
exps-for-one-empty-line = 0
ignore =
D100,
D104,

per-file-ignores =
tests/*:
# Missing docstring in public class
D101,
# Missing docstring in public method
D102,
# Missing docstring in public function
D103,
# Missing docstring in magic method
D105,
# Missing docstring in __init__
D107,
# Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
S101,
# Found magic number
WPS432,
# Found wrong keyword: pass
WPS420,
# Found incorrect node inside `class` body
WPS604,
# Found outer scope names shadowing: message_update
WPS442,
# Found comparison with float or complex number
WPS459,
# split between test action and assert
WPS473,
# Found compare with falsy constant
WPS520,
# Found string literal over-use
WPS226
# Found overused expression
WPS204
# Found too many module members
WPS202

[mypy]
ignore_missing_imports = True
check_untyped_defs = True
disallow_untyped_calls = True
Empty file.
170 changes: 170 additions & 0 deletions Industrial_developed_hangman/src/hangman/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import random
import time
from enum import Enum
from pathlib import Path
from typing import Callable, List

import requests
from bs4 import BeautifulSoup
from colorama import Fore, Style

DEBUG = False
success_code = 200
request_timeout = 1000
data_path = Path(__file__).parent.parent.parent / 'Data'
year = 4800566455


class Source(Enum):
"""Enum that represents switch between local and web word parsing."""

FROM_FILE = 0 # noqa: WPS115
FROM_INTERNET = 1 # noqa: WPS115


def print_wrong(text: str, print_function: Callable[[str], None]) -> None:
"""
Print styled text(red).
:parameter text: text to print.
:parameter print_function: Function that will be used to print in game.
"""
text_to_print = Style.RESET_ALL + Fore.RED + text
print_function(text_to_print)


def print_right(text: str, print_function: Callable[[str], None]) -> None:
"""
Print styled text(red).
:parameter text: text to print.
:parameter print_function: Function that will be used to print in game.
"""
print_function(Style.RESET_ALL + Fore.GREEN + text)


def parse_word_from_local(choice_function: Callable[[List[str]], str] = random.choice) -> str:
# noqa: DAR201
"""
Parse word from local file.
:parameter choice_function: Function that will be used to choice a word from file.
:returns str: string that contains the word.
:raises FileNotFoundError: file to read words not found.
"""
try:
with open(data_path / 'local_words.txt', encoding='utf8') as words_file:
return choice_function(words_file.read().split('\n'))
except FileNotFoundError:
raise FileNotFoundError('File local_words.txt was not found')


def parse_word_from_site(url: str = 'https://randomword.com') -> str:
# noqa: DAR201
"""
Parse word from website.
:param url: url that word will be parsed from.
:return Optional[str]: string that contains the word.
:raises ConnectionError: no connection to the internet.
:raises RuntimeError: something go wrong with getting the word from site.
"""
try:
page: requests.Response = requests.get(url, timeout=request_timeout)
except ConnectionError:
raise ConnectionError('There is no connection to the internet')
if page.status_code == success_code:
soup = BeautifulSoup(page.text, 'html.parser')
return soup.find('div', id='random_word').text
raise RuntimeError('Something go wrong with getting the word from site')


class MainProcess(object):
"""Manages game process."""

def __init__(self, source: Enum, pr_func: Callable, in_func: Callable, ch_func: Callable) -> None:
"""
Init MainProcess object.
:parameter in_func: Function that will be used to get input in game.
:parameter source: Represents source to get word.
:parameter pr_func: Function that will be used to print in game.
:parameter ch_func: Function that will be used to choice word.
"""
self._source = source
self._answer_word = ''
self._word_string_to_show = ''
self._guess_attempts_coefficient = 2
self._print_function = pr_func
self._input_function = in_func
self._choice_function = ch_func

def get_word(self) -> str:
# noqa: DAR201
"""
Parse word(wrapper for local and web parse).
:returns str: string that contains the word.
:raises AttributeError: Not existing enum
"""
if self._source == Source.FROM_INTERNET:
return parse_word_from_site()
elif self._source == Source.FROM_FILE:
return parse_word_from_local(self._choice_function)
raise AttributeError('Non existing enum')

def user_lose(self) -> None:
"""Print text for end of game and exits."""
print_wrong(f"YOU LOST(the word was '{self._answer_word}')", self._print_function) # noqa:WPS305

def user_win(self) -> None:
"""Print text for end of game and exits."""
print_wrong(f'{self._word_string_to_show} YOU WON', self._print_function) # noqa:WPS305

def game_process(self, user_character: str) -> bool:
# noqa: DAR201
"""
Process user input.
:parameter user_character: User character.
:returns bool: state of game.
"""
if user_character in self._answer_word:
word_list_to_show = list(self._word_string_to_show)
for index, character in enumerate(self._answer_word):
if character == user_character:
word_list_to_show[index] = user_character
self._word_string_to_show = ''.join(word_list_to_show)
else:
print_wrong('There is no such character in word', self._print_function)
if self._answer_word == self._word_string_to_show:
self.user_win()
return True
return False

def start_game(self) -> None:
"""Start main process of the game."""
if time.time() > year:
print_right('this program is more then 100years age', self._print_function)
with open(data_path / 'text_images.txt', encoding='utf8') as text_images_file:
print_wrong(text_images_file.read(), self._print_function)
print_wrong('Start guessing...', self._print_function)
self._answer_word = self.get_word()
self._word_string_to_show = '_' * len(self._answer_word)
attempts_amount = int(self._guess_attempts_coefficient * len(self._answer_word))
if DEBUG:
print_right(self._answer_word, self._print_function)
for attempts in range(attempts_amount):
user_remaining_attempts = attempts_amount - attempts
print_right(f'You have {user_remaining_attempts} more attempts', self._print_function) # noqa:WPS305
print_right(f'{self._word_string_to_show} enter character to guess: ', self._print_function) # noqa:WPS305
user_character = self._input_function().lower()
if self.game_process(user_character):
break
if '_' in self._word_string_to_show:
self.user_lose()


if __name__ == '__main__':
main_process = MainProcess(Source(1), print, input, random.choice)
main_process.start_game()

0 comments on commit 60078fe

Please sign in to comment.