├── .coveragerc ├── .cruft.json ├── .github ├── FUNDING.yml └── workflows │ ├── lint.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── art ├── example.gif ├── logo.png ├── logo.xcf ├── logo_large.png ├── logo_large.xcf └── stylesheets │ └── extra.css ├── docs └── contributing │ ├── 1.-contributing-guide.md │ ├── 2.-coding-standard.md │ ├── 3.-code-of-conduct.md │ └── 4.-acknowledgements.md ├── mypy.ini ├── pyproject.toml ├── quickpython ├── __init__.py ├── cli.py ├── examples │ ├── __init__.py │ ├── connect.py │ ├── eightpuzzle.py │ ├── hangman.py │ ├── memory.py │ ├── minesweeper.py │ ├── simon.py │ ├── tictactoe.py │ ├── towers.py │ ├── uno.py │ └── zigzag.py └── extensions.py ├── scripts ├── clean.sh ├── done.sh ├── lint.sh └── test.sh ├── setup.cfg └── tests ├── __init__.py └── test_importable.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | exclude_lines = 3 | pragma: no cover 4 | omit = 5 | *tests* 6 | -------------------------------------------------------------------------------- /.cruft.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": "https://github.com/timothycrosley/cookiecutter-python/", 3 | "commit": "9be01014cde7ec06bd52415655d52ca30a9e9bcc", 4 | "context": { 5 | "cookiecutter": { 6 | "full_name": "Timothy Crosley", 7 | "email": "timothy.crosley@gmail.com", 8 | "github_username": "timothycrosley", 9 | "project_name": "quickpython", 10 | "description": "A retro interactive coding environment powered by Python and nostalgia ", 11 | "version": "0.0.1", 12 | "_template": "https://github.com/timothycrosley/cookiecutter-python/" 13 | } 14 | }, 15 | "directory": null 16 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: "timothycrosley" 2 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: [3.8] 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: pip cache 16 | uses: actions/cache@v1 17 | with: 18 | path: ~/.cache/pip 19 | key: lint-pip-${{ hashFiles('**/pyproject.toml') }} 20 | restore-keys: | 21 | lint-pip- 22 | 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v1 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | python -m pip install --upgrade poetry 32 | poetry install 33 | 34 | - name: Lint 35 | run: ./scripts/lint.sh 36 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | python-version: [3.6, 3.7, 3.8] 12 | os: [ubuntu-latest, ubuntu-18.04, macos-latest, windows-latest] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Ubuntu cache 17 | uses: actions/cache@v1 18 | if: startsWith(matrix.os, 'ubuntu') 19 | with: 20 | path: ~/.cache/pip 21 | key: 22 | ${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} 23 | restore-keys: | 24 | ${{ matrix.os }}-${{ matrix.python-version }}- 25 | 26 | - name: macOS cache 27 | uses: actions/cache@v1 28 | if: startsWith(matrix.os, 'macOS') 29 | with: 30 | path: ~/Library/Caches/pip 31 | key: 32 | ${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} 33 | restore-keys: | 34 | ${{ matrix.os }}-${{ matrix.python-version }}- 35 | 36 | - name: Windows cache 37 | uses: actions/cache@v1 38 | if: startsWith(matrix.os, 'windows') 39 | with: 40 | path: c:\users\runneradmin\appdata\local\pip\cache 41 | key: 42 | ${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} 43 | restore-keys: | 44 | ${{ matrix.os }}-${{ matrix.python-version }}- 45 | 46 | - name: Set up Python ${{ matrix.python-version }} 47 | uses: actions/setup-python@v1 48 | with: 49 | python-version: ${{ matrix.python-version }} 50 | 51 | - name: Install dependencies 52 | run: | 53 | python -m pip install --upgrade pip 54 | python -m pip install --upgrade poetry 55 | poetry install 56 | - name: Test 57 | shell: bash 58 | run: | 59 | poetry run pytest tests/ -s --cov=quickpython/ --cov-report=term-missing ${@-} 60 | poetry run coverage xml 61 | - name: Report Coverage 62 | if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.8' 63 | uses: codecov/codecov-action@v1.0.6 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | .DS_Store 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | build 10 | eggs 11 | .eggs 12 | parts 13 | var 14 | sdist 15 | develop-eggs 16 | .installed.cfg 17 | lib 18 | lib64 19 | MANIFEST 20 | 21 | # Installer logs 22 | pip-log.txt 23 | npm-debug.log 24 | pip-selfcheck.json 25 | 26 | # Unit test / coverage reports 27 | .coverage 28 | .tox 29 | nosetests.xml 30 | htmlcov 31 | .cache 32 | .pytest_cache 33 | .mypy_cache 34 | 35 | # Translations 36 | *.mo 37 | 38 | # Mr Developer 39 | .mr.developer.cfg 40 | .project 41 | .pydevproject 42 | 43 | # SQLite 44 | test_exp_framework 45 | 46 | # npm 47 | node_modules/ 48 | 49 | # dolphin 50 | .directory 51 | libpeerconnection.log 52 | 53 | # setuptools 54 | dist 55 | 56 | # IDE Files 57 | atlassian-ide-plugin.xml 58 | .idea/ 59 | *.swp 60 | *.kate-swp 61 | .ropeproject/ 62 | 63 | # Python3 Venv Files 64 | .venv/ 65 | bin/ 66 | include/ 67 | lib/ 68 | lib64 69 | pyvenv.cfg 70 | share/ 71 | venv/ 72 | .python-version 73 | 74 | # Cython 75 | *.c 76 | 77 | # Emacs backup 78 | *~ 79 | 80 | # VSCode 81 | /.vscode 82 | 83 | # qpython 84 | *.qpython 85 | 86 | # Automatically generated files 87 | docs/preconvert 88 | site/ 89 | out 90 | poetry.lock 91 | 92 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Install the latest 2 | =================== 3 | 4 | To install the latest version of quickpython simply run: 5 | 6 | `pip3 install quickpython` 7 | 8 | OR 9 | 10 | `poetry add quickpython` 11 | 12 | OR 13 | 14 | `pipenv install quickpython` 15 | 16 | 17 | Changelog 18 | ========= 19 | ## 1.0.0 - TBD 20 | - Initial API stable release. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Timothy Crosley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![QuickPYTHON - Educational Interactive Coding Environment](https://raw.githubusercontent.com/timothycrosley/quickpython/main/art/logo_large.png)](https://timothycrosley.github.io/quickpython/) 2 | _________________ 3 | 4 | [![PyPI version](https://badge.fury.io/py/quickpython.svg)](http://badge.fury.io/py/quickpython) 5 | [![Test Status](https://github.com/timothycrosley/quickpython/workflows/Test/badge.svg?branch=develop)](https://github.com/timothycrosley/quickpython/actions?query=workflow%3ATest) 6 | [![Lint Status](https://github.com/timothycrosley/quickpython/workflows/Lint/badge.svg?branch=develop)](https://github.com/timothycrosley/quickpython/actions?query=workflow%3ALint) 7 | [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.python.org/pypi/quickpython/) 8 | [![Downloads](https://pepy.tech/badge/quickpython)](https://pepy.tech/project/quickpython) 9 | [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 10 | [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://timothycrosley.github.io/isort/) 11 | [![codecov](https://codecov.io/gh/timothycrosley/quickpython/branch/main/graph/badge.svg)](https://codecov.io/gh/timothycrosley/quickpython) 12 | [![Join the chat at https://gitter.im/timothycrosley/quickpython](https://badges.gitter.im/timothycrosley/quickpython.svg)](https://gitter.im/timothycrosley/quickpython?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 13 | _________________ 14 | 15 | [Read Latest Documentation](https://timothycrosley.github.io/quickpython/) - [Browse GitHub Code Repository](https://github.com/timothycrosley/quickpython/) 16 | _________________ 17 | 18 | ![Example Usage](https://raw.githubusercontent.com/timothycrosley/quickpython/main/art/example.gif) 19 | 20 | **QuickPYTHON** A retro-futuristic educational interactive coding environment. Powered by Python and nostalgia. 21 | 22 | Key features 23 | 24 | - Mouse support 25 | - Futuristic blue color scheme 26 | - Auto-formatting 27 | - Integrated Debugging Support 28 | - Quick shortcuts for creating new dataclasses, static methods, etc 29 | - Built-in help 30 | - Games! 31 | 32 | ## Quick Start Instructions 33 | 34 | ```bash 35 | pip install quickpython 36 | ``` 37 | 38 | then start with 39 | 40 | ```bash 41 | qpython 42 | ``` 43 | 44 | or 45 | 46 | ```bash 47 | quickpython 48 | ``` 49 | 50 | *Disclaimer*: This project is provided as-is, for fun, with no guarantee of long-term support or maintenance. 51 | -------------------------------------------------------------------------------- /art/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/quickpython/adbf79947f9c9e094b5a32bf72c056bfcc7c623d/art/example.gif -------------------------------------------------------------------------------- /art/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/quickpython/adbf79947f9c9e094b5a32bf72c056bfcc7c623d/art/logo.png -------------------------------------------------------------------------------- /art/logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/quickpython/adbf79947f9c9e094b5a32bf72c056bfcc7c623d/art/logo.xcf -------------------------------------------------------------------------------- /art/logo_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/quickpython/adbf79947f9c9e094b5a32bf72c056bfcc7c623d/art/logo_large.png -------------------------------------------------------------------------------- /art/logo_large.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/quickpython/adbf79947f9c9e094b5a32bf72c056bfcc7c623d/art/logo_large.xcf -------------------------------------------------------------------------------- /art/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | [data-md-color-scheme="quickpython"] { 2 | --md-primary-fg-color: #0000AF; 3 | --md-primary-fg-color--light: #1674B1; 4 | --md-primary-fg-color--dark: #1674b1; 5 | } 6 | -------------------------------------------------------------------------------- /docs/contributing/1.-contributing-guide.md: -------------------------------------------------------------------------------- 1 | Contributing to quickpython 2 | ======== 3 | 4 | Looking for a useful open source project to contribute to? 5 | Want your contributions to be warmly welcomed and acknowledged? 6 | Welcome! You have found the right place. 7 | 8 | ## Getting quickpython set up for local development 9 | The first step when contributing to any project is getting it set up on your local machine. quickpython aims to make this as simple as possible. 10 | 11 | Account Requirements: 12 | 13 | - [A valid GitHub account](https://github.com/join) 14 | 15 | Base System Requirements: 16 | 17 | - Python3.6+ 18 | - poetry 19 | - bash or a bash compatible shell (should be auto-installed on Linux / Mac) 20 | 21 | Once you have verified that you system matches the base requirements you can start to get the project working by following these steps: 22 | 23 | 1. [Fork the project on GitHub](https://github.com/timothycrosley/quickpython/fork). 24 | 2. Clone your fork to your local file system: 25 | `git clone https://github.com/$GITHUB_ACCOUNT/quickpython.git` 26 | 3. `cd quickpython 27 | 4. `poetry install` 28 | 29 | ## Making a contribution 30 | Congrats! You're now ready to make a contribution! Use the following as a guide to help you reach a successful pull-request: 31 | 32 | 1. Check the [issues page](https://github.com/timothycrosley/quickpython/issues) on GitHub to see if the task you want to complete is listed there. 33 | - If it's listed there, write a comment letting others know you are working on it. 34 | - If it's not listed in GitHub issues, go ahead and log a new issue. Then add a comment letting everyone know you have it under control. 35 | - If you're not sure if it's something that is good for the main quickpython project and want immediate feedback, you can discuss it [here](https://gitter.im/timothycrosley/quickpython). 36 | 2. Create an issue branch for your local work `git checkout -b issue/$ISSUE-NUMBER`. 37 | 3. Do your magic here. 38 | 4. Ensure your code matches the [HOPE-8 Coding Standard](https://github.com/hugapi/HOPE/blob/master/all/HOPE-8--Style-Guide-for-Hug-Code.md#hope-8----style-guide-for-hug-code) used by the project. 39 | 5. Submit a pull request to the main project repository via GitHub. 40 | 41 | Thanks for the contribution! It will quickly get reviewed, and, once accepted, will result in your name being added to the acknowledgments list :). 42 | 43 | ## Thank you! 44 | I can not tell you how thankful I am for the hard work done by quickpython contributors like *you*. 45 | 46 | Thank you! 47 | 48 | ~Timothy Crosley 49 | 50 | -------------------------------------------------------------------------------- /docs/contributing/2.-coding-standard.md: -------------------------------------------------------------------------------- 1 | # HOPE 8 -- Style Guide for Hug Code 2 | 3 | | | | 4 | | ------------| ------------------------------------------- | 5 | | HOPE: | 8 | 6 | | Title: | Style Guide for Hug Code | 7 | | Author(s): | Timothy Crosley | 8 | | Status: | Active | 9 | | Type: | Process | 10 | | Created: | 19-May-2019 | 11 | | Updated: | 17-August-2019 | 12 | 13 | ## Introduction 14 | 15 | This document gives coding conventions for the Hug code comprising the Hug core as well as all official interfaces, extensions, and plugins for the framework. 16 | Optionally, projects that use Hug are encouraged to follow this HOPE and link to it as a reference. 17 | 18 | ## PEP 8 Foundation 19 | 20 | All guidelines in this document are in addition to those defined in Python's [PEP 8](https://www.python.org/dev/peps/pep-0008/) and [PEP 257](https://www.python.org/dev/peps/pep-0257/) guidelines. 21 | 22 | ## Line Length 23 | 24 | Too short of lines discourage descriptive variable names where they otherwise make sense. 25 | Too long of lines reduce overall readability and make it hard to compare 2 files side by side. 26 | There is no perfect number: but for Hug, we've decided to cap the lines at 100 characters. 27 | 28 | ## Descriptive Variable names 29 | 30 | Naming things is hard. Hug has a few strict guidelines on the usage of variable names, which hopefully will reduce some of the guesswork: 31 | - No one character variable names. 32 | - Except for x, y, and z as coordinates. 33 | - It's not okay to override built-in functions. 34 | - Except for `id`. Guido himself thought that shouldn't have been moved to the system module. It's too commonly used, and alternatives feel very artificial. 35 | - Avoid Acronyms, Abbreviations, or any other short forms - unless they are almost universally understand. 36 | 37 | ## Adding new modules 38 | 39 | New modules added to the a project that follows the HOPE-8 standard should all live directly within the base `PROJECT_NAME/` directory without nesting. If the modules are meant only for internal use within the project, they should be prefixed with a leading underscore. For example, def _internal_function. Modules should contain a docstring at the top that gives a general explanation of the purpose and then restates the project's use of the MIT license. 40 | There should be a `tests/test_$MODULE_NAME.py` file created to correspond to every new module that contains test coverage for the module. Ideally, tests should be 1:1 (one test object per code object, one test method per code method) to the extent cleanly possible. 41 | 42 | ## Automated Code Cleaners 43 | 44 | All code submitted to Hug should be formatted using Black and isort. 45 | Black should be run with the line length set to 100, and isort with Black compatible settings in place. 46 | 47 | ## Automated Code Linting 48 | 49 | All code submitted to hug should run through the following tools: 50 | 51 | - Black and isort verification. 52 | - Flake8 53 | - flake8-bugbear 54 | - Bandit 55 | - pep8-naming 56 | - vulture 57 | - safety 58 | -------------------------------------------------------------------------------- /docs/contributing/3.-code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # HOPE 11 -- Code of Conduct 2 | 3 | | | | 4 | | ------------| ------------------------------------------- | 5 | | HOPE: | 11 | 6 | | Title: | Code of Conduct | 7 | | Author(s): | Timothy Crosley | 8 | | Status: | Active | 9 | | Type: | Process | 10 | | Created: | 17-August-2019 | 11 | | Updated: | 17-August-2019 | 12 | 13 | ## Abstract 14 | 15 | Defines the Code of Conduct for Hug and all related projects. 16 | 17 | ## Our Pledge 18 | 19 | In the interest of fostering an open and welcoming environment, we as 20 | contributors and maintainers pledge to making participation in our project and 21 | our community a harassment-free experience for everyone, regardless of age, body 22 | size, disability, ethnicity, sex characteristics, gender identity and expression, 23 | level of experience, education, socio-economic status, nationality, personal 24 | appearance, race, religion, or sexual identity and orientation. 25 | 26 | ## Our Standards 27 | 28 | Examples of behavior that contributes to creating a positive environment 29 | include: 30 | 31 | * Using welcoming and inclusive language 32 | * Being respectful of differing viewpoints and experiences 33 | * Gracefully accepting constructive criticism 34 | * Focusing on what is best for the community 35 | * Showing empathy towards other community members 36 | 37 | Examples of unacceptable behavior by participants include: 38 | 39 | * The use of sexualized language or imagery and unwelcome sexual attention or 40 | advances 41 | * Trolling, insulting/derogatory comments, and personal or political attacks 42 | * Public or private harassment 43 | * Publishing others' private information, such as a physical or electronic 44 | address, without explicit permission 45 | * Other conduct which could reasonably be considered inappropriate in a 46 | professional setting 47 | 48 | ## Our Responsibilities 49 | 50 | Project maintainers are responsible for clarifying the standards of acceptable 51 | behavior and are expected to take appropriate and fair corrective action in 52 | response to any instances of unacceptable behavior. 53 | 54 | Project maintainers have the right and responsibility to remove, edit, or 55 | reject comments, commits, code, wiki edits, issues, and other contributions 56 | that are not aligned to this Code of Conduct, or to ban temporarily or 57 | permanently any contributor for other behaviors that they deem inappropriate, 58 | threatening, offensive, or harmful. 59 | 60 | ## Scope 61 | 62 | This Code of Conduct applies both within project spaces and in public spaces 63 | when an individual is representing the project or its community. Examples of 64 | representing a project or community include using an official project e-mail 65 | address, posting via an official social media account, or acting as an appointed 66 | representative at an online or offline event. Representation of a project may be 67 | further defined and clarified by project maintainers. 68 | 69 | ## Enforcement 70 | 71 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 72 | reported by contacting [timothy.crosley@gmail.com](mailto:timothy.crosley@gmail.com). All 73 | complaints will be reviewed and investigated and will result in a response that 74 | is deemed necessary and appropriate to the circumstances. Confidentiality will be maintained 75 | with regard to the reporter of an incident. 76 | Further details of specific enforcement policies may be posted separately. 77 | 78 | Project maintainers who do not follow or enforce the Code of Conduct in good 79 | faith may face temporary or permanent repercussions as determined by other 80 | members of the project's leadership. 81 | 82 | ## Attribution 83 | 84 | This Code of Conduct is adapted from the [Contributor Covenant][https://www.contributor-covenant.org], version 1.4, 85 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 86 | 87 | For answers to common questions about this code of conduct, see 88 | https://www.contributor-covenant.org/faq 89 | -------------------------------------------------------------------------------- /docs/contributing/4.-acknowledgements.md: -------------------------------------------------------------------------------- 1 | Contributors 2 | =================== 3 | 4 | ## Core Developers 5 | - Timothy Crosley (@timothycrosley) 6 | 7 | ## Notable Bug Reporters 8 | - 9 | 10 | ## Code Contributors 11 | - 12 | 13 | ## Documenters 14 | - 15 | 16 | 17 | -------------------------------------------- 18 | 19 | A sincere thanks to everyone who helps make quickpython into a great Python3 project! 20 | 21 | ~Timothy Crosley 22 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | python_version = 3.7 3 | 4 | [mypy-quickpython.examples.*] 5 | ignore_errors = True 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "quickpython" 3 | version = "0.1.4" 4 | description = "A retro interactive coding environment powered by Python and nostalgia " 5 | authors = ["Timothy Crosley "] 6 | license = "MIT" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.6.1" 11 | prompt-toolkit = "^3.0.6" 12 | isort = "^5.4.2" 13 | black = {version = "^20.8b1", allow-prereleases = true} 14 | ipdb = "^0.13.4" 15 | colorama = "^0.4.4" 16 | pyfiglet = "^0.8.post1" 17 | 18 | [tool.poetry.scripts] 19 | quickpython = "quickpython.cli:start" 20 | qpython = "quickpython.cli:start" 21 | qp = "quickpython.cli:start" 22 | 23 | [tool.poetry.dev-dependencies] 24 | vulture = "^1.0" 25 | bandit = "^1.6" 26 | safety = "^1.8" 27 | isort = "^5.3" 28 | flake8-bugbear = "^19.8" 29 | mypy = "^0.730.0" 30 | ipython = "^7.7" 31 | pytest = "^5.0" 32 | pytest-cov = "^2.7" 33 | pytest-mock = "^1.10" 34 | pep8-naming = "^0.8.2" 35 | portray = "^1.3.0" 36 | cruft = "^2.2" 37 | flake8 = "^3.8.3" 38 | 39 | [build-system] 40 | requires = ["poetry>=0.12"] 41 | build-backend = "poetry.masonry.api" 42 | 43 | [tool.black] 44 | line-length = 100 45 | 46 | [tool.isort] 47 | profile = "hug" 48 | 49 | [tool.portray.mkdocs] 50 | edit_uri = "https://github.com/timothycrosley/quickpython/edit/develop/" 51 | extra_css = ["art/stylesheets/extra.css"] 52 | 53 | [tool.portray.mkdocs.theme] 54 | name = "material" 55 | favicon = "art/logo.png" 56 | logo = "art/logo.png" 57 | palette = {scheme = "quickpython"} 58 | -------------------------------------------------------------------------------- /quickpython/__init__.py: -------------------------------------------------------------------------------- 1 | """**quickpython** 2 | 3 | A retro interactive coding environment powered by Python and nostalgia 4 | """ 5 | 6 | __version__ = "0.1.4" 7 | -------------------------------------------------------------------------------- /quickpython/cli.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import builtins 3 | import dataclasses 4 | import os 5 | import pydoc 6 | import sys 7 | import types 8 | from asyncio import Future, ensure_future 9 | from datetime import datetime 10 | from functools import partial 11 | from pathlib import Path 12 | from typing import Optional 13 | 14 | import black 15 | import isort 16 | from prompt_toolkit import Application 17 | from prompt_toolkit.completion import PathCompleter 18 | from prompt_toolkit.filters import Condition 19 | from prompt_toolkit.formatted_text import AnyFormattedText, Template 20 | from prompt_toolkit.key_binding.key_bindings import KeyBindings 21 | from prompt_toolkit.layout.containers import ( 22 | AnyContainer, 23 | ConditionalContainer, 24 | Container, 25 | DynamicContainer, 26 | Float, 27 | HSplit, 28 | VSplit, 29 | Window, 30 | ) 31 | from prompt_toolkit.layout.dimension import AnyDimension, Dimension 32 | from prompt_toolkit.layout.layout import Layout 33 | from prompt_toolkit.layout.menus import CompletionsMenu 34 | from prompt_toolkit.output.color_depth import ColorDepth 35 | from prompt_toolkit.search import start_search 36 | from prompt_toolkit.shortcuts import clear, message_dialog 37 | from prompt_toolkit.styles import Style 38 | from prompt_toolkit.widgets import Dialog, MenuContainer, MenuItem, SearchToolbar, TextArea 39 | from prompt_toolkit.widgets.base import Border, Button, Label 40 | 41 | from quickpython import __version__, extensions # noqa 42 | 43 | ABOUT_MESSAGE = f"""QuickPython version {__version__} 44 | 45 | Copyright (c) 2020 Timothy Crosley. Few rights reserved. MIT Licensed. 46 | Simultanously distributed to the US and Canada. 47 | And you know, the rest of the world. 48 | 49 | A productive parody. 50 | Made in Seattle. 51 | """ 52 | 53 | BLACK_ALLOWED_MODE_KEYS = {field.name for field in dataclasses.fields(black.FileMode)} 54 | 55 | kb = KeyBindings() 56 | eb = KeyBindings() 57 | current_file: Optional[Path] = None 58 | 59 | default_isort_config = isort.Config(settings_path=os.getcwd()) 60 | if default_isort_config == isort.settings.DEFAULT_CONFIG: 61 | default_isort_config = isort.Config(profile="black", float_to_top=True) 62 | 63 | default_black_config_file = black.find_pyproject_toml((os.getcwd(),)) 64 | if default_black_config_file: 65 | default_black_config = black.parse_pyproject_toml(default_black_config_file) 66 | else: 67 | default_black_config = {} 68 | 69 | isort_config: isort.Config = default_isort_config 70 | black_config: dict = default_black_config 71 | 72 | code_frame_style = Style.from_dict({"frame.label": "bg:#AAAAAA fg:#0000aa"}) 73 | style = Style.from_dict( 74 | { 75 | "menu-bar": "bg:#aaaaaa black bold", 76 | "menu-bar.selected-item": "bg:black #aaaaaa bold", 77 | "menu": "bg:#aaaaaa black bold", 78 | "menu.border shadow": "black", 79 | "shadow": "bg:black", 80 | "dialog": "bg:#0000AA", 81 | "frame.label": "fg:#AAAAAA bold", 82 | "dialog frame.label": "fg:black bold bg:#AAAAAA", 83 | "code-frame frame.label": "bg:#AAAAAA fg:#0000aa", 84 | "dialog.body": "bg:#AAAAAA fg:#000000", 85 | "dialog shadow": "bg:#000000", 86 | "scrollbar.background": "bg:#AAAAAA", 87 | "scrollbar.button": "bg:black fg:black", 88 | "scrollbar.arrow": "bg:#AAAAAA fg:black bold", 89 | "": "bg:#0000AA fg:#AAAAAA bold", 90 | } 91 | ) 92 | 93 | 94 | @kb.add("escape") 95 | def _(event): 96 | """Focus the menu""" 97 | if event.app.layout.has_focus(root_container.window): 98 | event.app.layout.focus(code) 99 | else: 100 | event.app.layout.focus(root_container.window) 101 | 102 | 103 | def format_code(contents: str) -> str: 104 | return black_format_code(isort_format_code(contents)) 105 | 106 | 107 | def isort_format_code(contents: str) -> str: 108 | return isort.code(contents, config=isort_config) 109 | 110 | 111 | class TextInputDialog: 112 | def __init__(self, title="", label_text="", completer=None): 113 | self.future = Future() 114 | 115 | def accept_text(buf): 116 | app.layout.focus(ok_button) 117 | buf.complete_state = None 118 | return True 119 | 120 | def accept(): 121 | self.future.set_result(self.text_area.text) 122 | 123 | def cancel(): 124 | self.future.set_result(None) 125 | 126 | self.text_area = TextArea( 127 | completer=completer, 128 | multiline=False, 129 | width=Dimension(preferred=40), 130 | accept_handler=accept_text, 131 | ) 132 | 133 | ok_button = Button(text="OK", handler=accept) 134 | cancel_button = Button(text="Cancel", handler=cancel) 135 | 136 | self.dialog = Dialog( 137 | title=title, 138 | body=HSplit([Label(text=label_text), self.text_area]), 139 | buttons=[ok_button, cancel_button], 140 | width=Dimension(preferred=80), 141 | modal=True, 142 | ) 143 | 144 | def __pt_container__(self): 145 | return self.dialog 146 | 147 | 148 | class MessageDialog: 149 | def __init__(self, title, text): 150 | self.future = Future() 151 | 152 | def set_done(): 153 | self.future.set_result(None) 154 | 155 | ok_button = Button(text="OK", handler=(lambda: set_done())) 156 | 157 | self.dialog = Dialog( 158 | title=title, 159 | body=HSplit([Label(text=text)]), 160 | buttons=[ok_button], 161 | width=Dimension(preferred=80), 162 | modal=True, 163 | ) 164 | 165 | def __pt_container__(self): 166 | return self.dialog 167 | 168 | 169 | async def show_dialog_as_float(dialog): 170 | " Coroutine. " 171 | float_ = Float(content=dialog) 172 | root_container.floats.insert(0, float_) 173 | 174 | focused_before = app.layout.current_window 175 | app.layout.focus(dialog) 176 | result = await dialog.future 177 | app.layout.focus(focused_before) 178 | 179 | if float_ in root_container.floats: 180 | root_container.floats.remove(float_) 181 | 182 | return result 183 | 184 | 185 | @kb.add("c-o") 186 | def open_file(event=None): 187 | async def coroutine(): 188 | global current_file 189 | global isort_config 190 | global black_config 191 | 192 | open_dialog = TextInputDialog( 193 | title="Open file", 194 | label_text="Enter the path of a file:", 195 | completer=PathCompleter(), 196 | ) 197 | 198 | filename = await show_dialog_as_float(open_dialog) 199 | 200 | if filename is not None: 201 | current_file = Path(filename).resolve() 202 | isort_config = isort.Config(settings_path=current_file.parent) 203 | black_config_file = black.find_pyproject_toml((str(current_file),)) 204 | if black_config_file: 205 | black_config = black.parse_pyproject_toml(black_config_file) 206 | else: 207 | black_config = {} 208 | 209 | try: 210 | with open(current_file, "r", encoding="utf8") as new_file_conent: 211 | code.buffer.text = new_file_conent.read() 212 | open_file_frame.title = current_file.name 213 | feedback(f"Successfully opened {current_file}") 214 | except IOError as error: 215 | feedback(f"Error: {error}") 216 | 217 | ensure_future(coroutine()) 218 | 219 | 220 | def save_as_file(): 221 | async def coroutine(): 222 | global current_file 223 | global isort_config 224 | global black_config 225 | 226 | save_dialog = TextInputDialog( 227 | title="Save file", 228 | label_text="Enter the path of a file:", 229 | completer=PathCompleter(), 230 | ) 231 | 232 | filename = await show_dialog_as_float(save_dialog) 233 | 234 | if filename is not None: 235 | current_file = Path(filename).resolve() 236 | isort_config = isort.Config(settings_path=current_file.parent) 237 | black_config_file = black.find_pyproject_toml((str(current_file),)) 238 | if black_config_file: 239 | black_config = black.parse_pyproject_toml(black_config_file) 240 | else: 241 | black_config = {} 242 | if not current_file.suffixes and not current_file.exists(): 243 | current_file = current_file.with_suffix(".py") 244 | open_file_frame.title = current_file.name 245 | save_file() 246 | 247 | ensure_future(coroutine()) 248 | 249 | 250 | def feedback(text): 251 | immediate.buffer.text = text 252 | 253 | 254 | def black_format_code(contents: str) -> str: 255 | """Formats the given import section using black.""" 256 | try: 257 | immediate.buffer.text = "" 258 | return black.format_file_contents( 259 | contents, 260 | fast=True, 261 | mode=black.FileMode( 262 | **{k: v for k, v in black_config.items() if k in BLACK_ALLOWED_MODE_KEYS} 263 | ), 264 | ) 265 | except black.NothingChanged: 266 | return contents 267 | except Exception as error: 268 | immediate.buffer.text = str(error) 269 | return contents 270 | 271 | 272 | def new(content=""): 273 | """Creates a new file buffer.""" 274 | global current_file 275 | global isort_config 276 | global black_config 277 | 278 | current_file = None 279 | isort_config = default_isort_config 280 | black_config = default_black_config 281 | code.buffer.text = content 282 | open_file_frame.title = "Untitled" 283 | feedback("") 284 | 285 | 286 | @kb.add("c-q") 287 | def exit(event=None): 288 | """Triggers the request to close QPython cleanly.""" 289 | app.exit() 290 | 291 | 292 | @Condition 293 | def is_code_focused() -> bool: 294 | return app.layout.has_focus(code) 295 | 296 | 297 | @kb.add("tab") 298 | def indent(event): 299 | event.app.current_buffer.insert_text(" ") 300 | 301 | 302 | @kb.add("enter", filter=is_code_focused) 303 | def enter(event): 304 | buffer = event.app.current_buffer 305 | buffer.insert_text("\n") 306 | if current_file and ".py" not in current_file.suffixes and ".pyi" not in current_file.suffixes: 307 | return 308 | 309 | old_cursor_position = buffer.cursor_position 310 | if old_cursor_position == 0: 311 | return 312 | 313 | end_position = buffer.text.rfind("\n", 0, old_cursor_position) + 1 314 | code, rest = buffer.text[:end_position], buffer.text[end_position:] 315 | if len(code) < 2 or (code[-1] == "\n" and code[-2] == "\n"): 316 | return 317 | 318 | formatted_code = format_code(code) 319 | difference = len(formatted_code) - len(code) 320 | buffer.text = formatted_code + rest 321 | buffer.cursor_position = old_cursor_position + difference 322 | 323 | 324 | @kb.add("c-s") 325 | def save_file(event=None): 326 | if not current_file: 327 | save_as_file() 328 | return 329 | 330 | buffer = app.current_buffer 331 | buffer.text = format_code(buffer.text) 332 | current_file.write_text(buffer.text, encoding="utf8") 333 | immediate.buffer.text = f"Successfully saved {current_file}" 334 | 335 | 336 | async def _run_buffer(debug: bool = False): 337 | buffer_filename = f"{current_file or 'buffer'}.qpython" 338 | with open(buffer_filename, "w") as buffer_file: 339 | user_code = app.current_buffer.text 340 | if not user_code.endswith("\n"): 341 | user_code += "\n" 342 | with_qpython_injected = isort.code(user_code, add_imports=["import quickpython.extensions"]) 343 | buffer_file.write(isort_format_code(with_qpython_injected)) 344 | if debug: 345 | buffer_file.write("breakpoint()") 346 | 347 | try: 348 | clear() 349 | os.environ["PYTHONBREAKPOINT"] = "ipdb.set_trace" 350 | await app.run_system_command(f'{sys.executable} "{buffer_filename}"') 351 | finally: 352 | os.remove(buffer_filename) 353 | 354 | 355 | async def _view_buffer(): 356 | clear() 357 | await app.run_system_command("echo ''") 358 | 359 | 360 | @kb.add("c-r") 361 | @kb.add("f5") 362 | def run_buffer(event=None): 363 | asyncio.ensure_future(_run_buffer()) 364 | 365 | 366 | def debug(): 367 | asyncio.ensure_future(_run_buffer(debug=True)) 368 | 369 | 370 | def view_buffer(event=None): 371 | asyncio.ensure_future(_view_buffer()) 372 | 373 | 374 | @kb.add("c-z") 375 | def undo(event=None): 376 | code.buffer.undo() 377 | 378 | 379 | def cut(): 380 | data = code.buffer.cut_selection() 381 | app.clipboard.set_data(data) 382 | 383 | 384 | def copy(): 385 | data = code.buffer.copy_selection() 386 | app.clipboard.set_data(data) 387 | 388 | 389 | def delete(): 390 | code.buffer.cut_selection() 391 | 392 | 393 | def paste(): 394 | code.buffer.paste_clipboard_data(app.clipboard.get_data()) 395 | 396 | 397 | @kb.add("c-a") 398 | def select_all(event=None): 399 | code.buffer.cursor_position = 0 400 | code.buffer.start_selection() 401 | code.buffer.cursor_position = len(code.buffer.text) 402 | 403 | 404 | def insert_time_and_date(): 405 | code.buffer.insert_text(datetime.now().isoformat()) 406 | 407 | 408 | search_toolbar = SearchToolbar() 409 | code = TextArea( 410 | scrollbar=True, 411 | wrap_lines=False, 412 | focus_on_click=True, 413 | line_numbers=True, 414 | search_field=search_toolbar, 415 | ) 416 | code.window.right_margins[0].up_arrow_symbol = "↑" # type: ignore 417 | code.window.right_margins[0].down_arrow_symbol = "↓" # type: ignore 418 | 419 | 420 | class CodeFrame: 421 | """A custom frame for the quick python code container to match desired styling""" 422 | 423 | def __init__( 424 | self, 425 | body: AnyContainer, 426 | title: AnyFormattedText = "", 427 | style: str = "", 428 | width: AnyDimension = None, 429 | height: AnyDimension = None, 430 | key_bindings: Optional[KeyBindings] = None, 431 | modal: bool = False, 432 | ) -> None: 433 | 434 | self.title = title 435 | self.body = body 436 | 437 | fill = partial(Window, style="class:frame.border") 438 | style = "class:frame " + style 439 | 440 | top_row_with_title = VSplit( 441 | [ 442 | fill(width=1, height=1, char=Border.TOP_LEFT), 443 | fill(char=Border.HORIZONTAL), 444 | Label( 445 | lambda: Template(" {} ").format(self.title), 446 | style="class:frame.label", 447 | dont_extend_width=True, 448 | ), 449 | fill(char=Border.HORIZONTAL), 450 | fill(width=1, height=1, char=Border.TOP_RIGHT), 451 | ], 452 | height=1, 453 | ) 454 | 455 | top_row_without_title = VSplit( 456 | [ 457 | fill(width=1, height=1, char=Border.TOP_LEFT), 458 | fill(char=Border.HORIZONTAL), 459 | fill(width=1, height=1, char=Border.TOP_RIGHT), 460 | ], 461 | height=1, 462 | ) 463 | 464 | @Condition 465 | def has_title() -> bool: 466 | return bool(self.title) 467 | 468 | self.container = HSplit( 469 | [ 470 | ConditionalContainer(content=top_row_with_title, filter=has_title), 471 | ConditionalContainer(content=top_row_without_title, filter=~has_title), 472 | VSplit( 473 | [ 474 | fill(width=1, char=Border.VERTICAL), 475 | DynamicContainer(lambda: self.body), 476 | fill(width=1, char=Border.VERTICAL), 477 | # Padding is required to make sure that if the content is 478 | # too small, the right frame border is still aligned. 479 | ], 480 | padding=0, 481 | ), 482 | ], 483 | width=width, 484 | height=height, 485 | style=style, 486 | key_bindings=key_bindings, 487 | modal=modal, 488 | ) 489 | 490 | def __pt_container__(self) -> Container: 491 | return self.container 492 | 493 | 494 | class ImmediateFrame: 495 | """ 496 | Draw a border around any container, optionally with a title text. 497 | Changing the title and body of the frame is possible at runtime by 498 | assigning to the `body` and `title` attributes of this class. 499 | :param body: Another container object. 500 | :param title: Text to be displayed in the top of the frame (can be formatted text). 501 | :param style: Style string to be applied to this widget. 502 | """ 503 | 504 | def __init__( 505 | self, 506 | body: AnyContainer, 507 | title: AnyFormattedText = "", 508 | style: str = "", 509 | width: AnyDimension = None, 510 | height: AnyDimension = None, 511 | key_bindings: Optional[KeyBindings] = None, 512 | modal: bool = False, 513 | ) -> None: 514 | 515 | self.title = title 516 | self.body = body 517 | 518 | fill = partial(Window, style="class:frame.border") 519 | style = "class:frame " + style 520 | 521 | top_row_with_title = VSplit( 522 | [ 523 | fill(width=1, height=1, char="├"), 524 | fill(char=Border.HORIZONTAL), 525 | # Notice: we use `Template` here, because `self.title` can be an 526 | # `HTML` object for instance. 527 | Label( 528 | lambda: Template(" {} ").format(self.title), 529 | style="class:frame.label", 530 | dont_extend_width=True, 531 | ), 532 | fill(char=Border.HORIZONTAL), 533 | fill(width=1, height=1, char="┤"), 534 | ], 535 | height=1, 536 | ) 537 | 538 | top_row_without_title = VSplit( 539 | [ 540 | fill(width=1, height=1, char=Border.TOP_LEFT), 541 | fill(char=Border.HORIZONTAL), 542 | fill(width=1, height=1, char=Border.TOP_RIGHT), 543 | ], 544 | height=1, 545 | ) 546 | 547 | @Condition 548 | def has_title() -> bool: 549 | return bool(self.title) 550 | 551 | self.container = HSplit( 552 | [ 553 | ConditionalContainer(content=top_row_with_title, filter=has_title), 554 | ConditionalContainer(content=top_row_without_title, filter=~has_title), 555 | VSplit( 556 | [ 557 | fill(width=1, char=Border.VERTICAL), 558 | DynamicContainer(lambda: self.body), 559 | fill(width=1, char=Border.VERTICAL), 560 | # Padding is required to make sure that if the content is 561 | # too small, the right frame border is still aligned. 562 | ], 563 | padding=0, 564 | ), 565 | ], 566 | width=width, 567 | height=height, 568 | style=style, 569 | key_bindings=key_bindings, 570 | modal=modal, 571 | ) 572 | 573 | def __pt_container__(self) -> Container: 574 | return self.container 575 | 576 | 577 | open_file_frame = CodeFrame( 578 | HSplit( 579 | [ 580 | # One window that holds the BufferControl with the default buffer on 581 | # the left. 582 | code, 583 | # A vertical line in the middle. We explicitly specify the width, to 584 | # make sure that the layout engine will not try to divide the whole 585 | # width by three for all these windows. The window will simply fill its 586 | # content by repeating this character. 587 | ], 588 | ), 589 | title="Untitled", 590 | style="class:code-frame", 591 | ) 592 | 593 | 594 | @kb.add("c-g") 595 | def goto(event=None): 596 | async def coroutine(): 597 | dialog = TextInputDialog(title="Go to line", label_text="Line number:") 598 | 599 | line_number = await show_dialog_as_float(dialog) 600 | if line_number is None: 601 | return 602 | 603 | try: 604 | line_number = int(line_number) 605 | except ValueError: 606 | feedback("Invalid line number") 607 | else: 608 | code.buffer.cursor_position = code.buffer.document.translate_row_col_to_index( 609 | line_number - 1, 0 610 | ) 611 | 612 | ensure_future(coroutine()) 613 | 614 | 615 | def replace_text(): 616 | async def coroutine(): 617 | to_replace_dialog = TextInputDialog(title="Text to Replace", label_text="original:") 618 | replacement_dialog = TextInputDialog(title="Replace With", label_text="replacement:") 619 | 620 | to_replace = await show_dialog_as_float(to_replace_dialog) 621 | if to_replace is None: 622 | return 623 | 624 | replacement = await show_dialog_as_float(replacement_dialog) 625 | if replacement is None: 626 | return 627 | 628 | code.buffer.text = format_code(code.buffer.text.replace(to_replace, replacement)) 629 | 630 | ensure_future(coroutine()) 631 | 632 | 633 | def about(): 634 | async def coroutine(): 635 | await show_dialog_as_float(MessageDialog("About QuickPython", ABOUT_MESSAGE)) 636 | 637 | ensure_future(coroutine()) 638 | 639 | 640 | def add_function(): 641 | async def coroutine(): 642 | dialog = TextInputDialog(title="Add Function", label_text="Function name:") 643 | 644 | function_name = await show_dialog_as_float(dialog) 645 | if not function_name: 646 | return 647 | 648 | code.buffer.insert_text( 649 | f""" 650 | def {function_name}(): 651 | pass 652 | """ 653 | ) 654 | code.buffer.text = format_code(code.buffer.text) 655 | 656 | ensure_future(coroutine()) 657 | 658 | 659 | def add_class(): 660 | async def coroutine(): 661 | dialog = TextInputDialog(title="Add Class", label_text="Class name:") 662 | 663 | class_name = await show_dialog_as_float(dialog) 664 | if not class_name: 665 | return 666 | 667 | code.buffer.insert_text( 668 | f""" 669 | class {class_name}: 670 | def __init__(self): 671 | pass 672 | """ 673 | ) 674 | code.buffer.text = format_code(code.buffer.text) 675 | 676 | ensure_future(coroutine()) 677 | 678 | 679 | def add_data_class(): 680 | async def coroutine(): 681 | dialog = TextInputDialog(title="Add Data Class", label_text="Class name:") 682 | 683 | class_name = await show_dialog_as_float(dialog) 684 | if not class_name: 685 | return 686 | 687 | code.buffer.insert_text( 688 | f''' 689 | @dataclass 690 | class {class_name}: 691 | """Comment""" 692 | ''' 693 | ) 694 | code.buffer.text = isort.code( 695 | code.buffer.text, 696 | add_imports=["from dataclasses import dataclass"], 697 | float_to_top=True, 698 | ) 699 | code.buffer.text = format_code(code.buffer.text) 700 | 701 | ensure_future(coroutine()) 702 | 703 | 704 | def add_method(): 705 | async def coroutine(): 706 | dialog = TextInputDialog(title="Add Method", label_text="Method name:") 707 | 708 | method_name = await show_dialog_as_float(dialog) 709 | if not method_name: 710 | return 711 | 712 | code.buffer.insert_text( 713 | f""" 714 | def {method_name}(self): 715 | pass 716 | """ 717 | ) 718 | code.buffer.text = format_code(code.buffer.text) 719 | 720 | ensure_future(coroutine()) 721 | 722 | 723 | def add_static_method(): 724 | async def coroutine(): 725 | dialog = TextInputDialog(title="Add Static Method", label_text="Method name:") 726 | 727 | method_name = await show_dialog_as_float(dialog) 728 | if not method_name: 729 | return 730 | 731 | code.buffer.insert_text( 732 | f""" 733 | @staticmethod 734 | def {method_name}(): 735 | pass 736 | """ 737 | ) 738 | code.buffer.text = format_code(code.buffer.text) 739 | 740 | ensure_future(coroutine()) 741 | 742 | 743 | def add_class_method(): 744 | async def coroutine(): 745 | dialog = TextInputDialog(title="Add Class Method", label_text="Method name:") 746 | 747 | method_name = await show_dialog_as_float(dialog) 748 | if not method_name: 749 | return 750 | 751 | code.buffer.insert_text( 752 | f""" 753 | @classmethod 754 | def {method_name}(cls): 755 | pass 756 | """ 757 | ) 758 | code.buffer.text = format_code(code.buffer.text) 759 | 760 | ensure_future(coroutine()) 761 | 762 | 763 | @kb.add("c-f") 764 | def search(event=None): 765 | start_search(code.control) 766 | 767 | 768 | def search_next(event=None): 769 | search_state = app.current_search_state 770 | 771 | cursor_position = code.buffer.get_search_position(search_state, include_current_position=False) 772 | code.buffer.cursor_position = cursor_position 773 | 774 | 775 | def example(game_name: str): 776 | import inspect 777 | 778 | from quickpython import examples 779 | 780 | def expand_example(): 781 | new(inspect.getsource(getattr(examples, game_name))) 782 | 783 | return expand_example 784 | 785 | 786 | def built_in_functions(): 787 | docs = [ 788 | pydoc.render_doc(builtin, renderer=pydoc.plaintext).split("\n", 1)[1] 789 | for builtin_name, builtin in vars(builtins).items() 790 | if type(builtin) in (types.FunctionType, types.BuiltinFunctionType) 791 | and not builtin_name.startswith("_") 792 | ] 793 | new("\n".join(docs)) 794 | 795 | 796 | QLabel = partial(Label, dont_extend_width=True) 797 | SPACE = QLabel(" ") 798 | 799 | immediate = TextArea() 800 | root_container = MenuContainer( 801 | body=HSplit( 802 | [ 803 | open_file_frame, 804 | search_toolbar, 805 | ImmediateFrame( 806 | immediate, 807 | title="Immediate", 808 | height=5, 809 | style="fg:#AAAAAA bold", 810 | ), 811 | VSplit( 812 | [ 813 | QLabel(""), 814 | SPACE, 815 | QLabel(""), 816 | SPACE, 817 | QLabel(""), 818 | ], 819 | style="bg:#00AAAA fg:white bold", 820 | height=1, 821 | ), 822 | ] 823 | ), 824 | menu_items=[ 825 | MenuItem( 826 | " File ", 827 | children=[ 828 | MenuItem("New...", handler=new), 829 | MenuItem("Open...", handler=open_file), 830 | MenuItem("Save", handler=save_file), 831 | MenuItem("Save as...", handler=save_as_file), 832 | MenuItem("-", disabled=True), 833 | MenuItem("Exit", handler=exit), 834 | ], 835 | ), 836 | MenuItem( 837 | " Edit ", 838 | children=[ 839 | MenuItem("Undo", handler=undo), 840 | MenuItem("Cut", handler=cut), 841 | MenuItem("Copy", handler=copy), 842 | MenuItem("Paste", handler=paste), 843 | MenuItem("Delete", handler=delete), 844 | MenuItem("-", disabled=True), 845 | MenuItem("Go To", handler=goto), 846 | MenuItem("Select All", handler=select_all), 847 | MenuItem("Add Time/Date", handler=insert_time_and_date), 848 | MenuItem("-", disabled=True), 849 | MenuItem("New Function", handler=add_function), 850 | MenuItem("New Class", handler=add_class), 851 | MenuItem("New Data Class", handler=add_data_class), 852 | MenuItem("New Method", handler=add_method), 853 | MenuItem("New Static Method", handler=add_static_method), 854 | MenuItem("New Class Method", handler=add_class_method), 855 | ], 856 | ), 857 | MenuItem( 858 | " View ", 859 | children=[MenuItem("Output Screen", handler=view_buffer)], 860 | ), 861 | MenuItem( 862 | " Search ", 863 | children=[ 864 | MenuItem("Find (CTRL+F)", handler=search), 865 | MenuItem("Repeat last find", handler=search_next), 866 | MenuItem("Change", handler=replace_text), 867 | ], 868 | ), 869 | MenuItem( 870 | " Run ", 871 | children=[ 872 | MenuItem("Start (F5)", handler=run_buffer), 873 | MenuItem("Debug", handler=debug), 874 | ], 875 | ), 876 | MenuItem( 877 | " Examples ", 878 | children=[ 879 | MenuItem("Connect", handler=example("connect")), 880 | MenuItem("Eight Puzzle", handler=example("eightpuzzle")), 881 | MenuItem("Hang Man", handler=example("hangman")), 882 | MenuItem("Memory", handler=example("memory")), 883 | MenuItem("Minesweeper", handler=example("minesweeper")), 884 | MenuItem("Simon", handler=example("simon")), 885 | MenuItem("Tic Tac Toe", handler=example("tictactoe")), 886 | MenuItem("Towers", handler=example("towers")), 887 | MenuItem("Zig Zag", handler=example("zigzag")), 888 | MenuItem("Uno", handler=example("uno")), 889 | ], 890 | ), 891 | MenuItem( 892 | " Help ", 893 | children=[ 894 | MenuItem("About", handler=about), 895 | MenuItem("Built-in Functions", handler=built_in_functions), 896 | ], 897 | ), 898 | ], 899 | floats=[ 900 | Float( 901 | xcursor=True, 902 | ycursor=True, 903 | content=CompletionsMenu(max_height=16, scroll_offset=1), 904 | ), 905 | ], 906 | key_bindings=kb, 907 | ) 908 | 909 | 910 | layout = Layout(root_container) 911 | 912 | app: Application = Application( 913 | layout=layout, 914 | full_screen=True, 915 | mouse_support=True, 916 | style=style, 917 | enable_page_navigation_bindings=True, 918 | color_depth=ColorDepth.DEPTH_8_BIT, 919 | ) 920 | 921 | 922 | def start(argv=None): 923 | global current_file 924 | global isort_config 925 | global black_config 926 | 927 | argv = sys.argv if argv is None else argv 928 | if len(sys.argv) > 2: 929 | sys.exit("Usage: qpython [filename]") 930 | elif len(sys.argv) == 2: 931 | current_file = Path(sys.argv[1]).resolve() 932 | isort_config = isort.Config(settings_path=current_file.parent) 933 | black_config_file = black.find_pyproject_toml((str(current_file),)) 934 | if black_config_file: 935 | black_config = black.parse_pyproject_toml(black_config_file) 936 | else: 937 | black_config = {} 938 | 939 | open_file_frame.title = current_file.name 940 | if current_file.exists(): 941 | with current_file.open(encoding="utf8") as open_file: 942 | code.buffer.text = open_file.read() 943 | else: 944 | message_dialog( 945 | title="Welcome to", 946 | text=ABOUT_MESSAGE, 947 | style=style, 948 | ).run() 949 | 950 | app.layout.focus(code.buffer) 951 | app.run() 952 | 953 | 954 | if __name__ == "__main__": 955 | start() 956 | -------------------------------------------------------------------------------- /quickpython/examples/__init__.py: -------------------------------------------------------------------------------- 1 | from quickpython.examples import ( 2 | connect, 3 | eightpuzzle, 4 | hangman, 5 | memory, 6 | minesweeper, 7 | simon, 8 | tictactoe, 9 | towers, 10 | uno, 11 | zigzag, 12 | ) 13 | 14 | __all__ = [ 15 | "connect", 16 | "eightpuzzle", 17 | "hangman", 18 | "memory", 19 | "minesweeper", 20 | "simon", 21 | "tictactoe", 22 | "towers", 23 | "uno", 24 | "zigzag", 25 | ] 26 | -------------------------------------------------------------------------------- /quickpython/examples/connect.py: -------------------------------------------------------------------------------- 1 | """ 2 | /games/connect.py 3 | 4 | Copyright (c) 2019 ShineyDev 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | 19 | __authors__ = [("shineydev", "contact@shiney.dev")] 20 | __maintainers__ = [("shineydev", "contact@shiney.dev")] 21 | 22 | __version_info__ = (0, 0, 1, "alpha", 0) 23 | __version__ = "{0}.{1}.{2}{3}{4}".format( 24 | *[str(n)[0] if (i == 3) else str(n) for (i, n) in enumerate(__version_info__)] 25 | ) 26 | 27 | 28 | import os 29 | 30 | import pyfiglet 31 | 32 | 33 | class Connect: 34 | def __init__(self): 35 | """ 36 | initializes a `Connect` object 37 | """ 38 | 39 | pass 40 | 41 | def game(self): 42 | """ 43 | starts the game 44 | """ 45 | 46 | pass 47 | 48 | def start(self): 49 | """ 50 | calls `self.game` in a 'would you like to play again?' loop 51 | """ 52 | 53 | choice = "y" 54 | while choice.startswith("y"): 55 | cls() 56 | print(pyfiglet.figlet_format("Connect 4")) 57 | print() 58 | input("enter to play\nctrl + c to quit to main menu\n\n") 59 | 60 | self.game() 61 | choice = input("\nwould you like to play again?\n> ").strip() 62 | 63 | 64 | if __name__ == "__main__": 65 | game = Connect() 66 | game.start() 67 | -------------------------------------------------------------------------------- /quickpython/examples/eightpuzzle.py: -------------------------------------------------------------------------------- 1 | """ 2 | /games/eightpuzzle.py 3 | 4 | Copyright (c) 2019 ShineyDev 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | 19 | __authors__ = [("shineydev", "contact@shiney.dev")] 20 | __maintainers__ = [("shineydev", "contact@shiney.dev")] 21 | 22 | __version_info__ = (0, 0, 1, "alpha", 0) 23 | __version__ = "{0}.{1}.{2}{3}{4}".format( 24 | *[str(n)[0] if (i == 3) else str(n) for (i, n) in enumerate(__version_info__)] 25 | ) 26 | 27 | 28 | import os 29 | 30 | import pyfiglet 31 | 32 | 33 | class EightPuzzle: 34 | def __init__(self): 35 | """ 36 | initializes an `EightPuzzle` object 37 | """ 38 | 39 | pass 40 | 41 | def game(self): 42 | """ 43 | starts the game 44 | """ 45 | 46 | pass 47 | 48 | def start(self): 49 | """ 50 | calls `self.game` in a 'would you like to play again?' loop 51 | """ 52 | 53 | choice = "y" 54 | while choice.startswith("y"): 55 | cls() 56 | print(pyfiglet.figlet_format("Eight Puzzle")) 57 | print() 58 | input("enter to play\nctrl + c to quit to main menu\n\n") 59 | 60 | self.game() 61 | choice = input("\nwould you like to play again?\n> ").strip() 62 | 63 | 64 | if __name__ == "__main__": 65 | game = EightPuzzle() 66 | game.start() 67 | -------------------------------------------------------------------------------- /quickpython/examples/hangman.py: -------------------------------------------------------------------------------- 1 | """ 2 | /games/hangman.py 3 | 4 | Copyright (c) 2019 ShineyDev 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | 19 | __authors__ = [("shineydev", "contact@shiney.dev")] 20 | __maintainers__ = [("shineydev", "contact@shiney.dev")] 21 | 22 | __version_info__ = (1, 0, 0, "final", 0) 23 | __version__ = "{0}.{1}.{2}{3}{4}".format( 24 | *[str(n)[0] if (i == 3) else str(n) for (i, n) in enumerate(__version_info__)] 25 | ) 26 | 27 | 28 | import os 29 | import random 30 | 31 | import pyfiglet 32 | 33 | HANGMAN = [ 34 | """ 35 | \t----- 36 | \t| | 37 | \t| 38 | \t| 39 | \t| 40 | \t| 41 | \t| 42 | \t| 43 | \t| 44 | \t-------- 45 | """, 46 | """ 47 | \t----- 48 | \t| | 49 | \t| 0 50 | \t| 51 | \t| 52 | \t| 53 | \t| 54 | \t| 55 | \t| 56 | \t-------- 57 | """, 58 | """ 59 | \t----- 60 | \t| | 61 | \t| 0 62 | \t| -+- 63 | \t| 64 | \t| 65 | \t| 66 | \t| 67 | \t| 68 | \t-------- 69 | """, 70 | """ 71 | \t----- 72 | \t| | 73 | \t| 0 74 | \t| /-+- 75 | \t| 76 | \t| 77 | \t| 78 | \t| 79 | \t| 80 | \t-------- 81 | """, 82 | """ 83 | \t----- 84 | \t| | 85 | \t| 0 86 | \t| /-+-\\ 87 | \t| 88 | \t| 89 | \t| 90 | \t| 91 | \t| 92 | \t-------- 93 | """, 94 | """ 95 | \t----- 96 | \t| | 97 | \t| 0 98 | \t| /-+-\\ 99 | \t| | 100 | \t| 101 | \t| 102 | \t| 103 | \t| 104 | \t-------- 105 | """, 106 | """ 107 | \t----- 108 | \t| | 109 | \t| 0 110 | \t| /-+-\\ 111 | \t| | 112 | \t| | 113 | \t| 114 | \t| 115 | \t| 116 | \t-------- 117 | """, 118 | """ 119 | \t----- 120 | \t| | 121 | \t| 0 122 | \t| /-+-\\ 123 | \t| | 124 | \t| | 125 | \t| | 126 | \t| 127 | \t| 128 | \t-------- 129 | """, 130 | """ 131 | \t----- 132 | \t| | 133 | \t| 0 134 | \t| /-+-\\ 135 | \t| | 136 | \t| | 137 | \t| | 138 | \t| | 139 | \t| 140 | \t-------- 141 | """, 142 | """ 143 | \t----- 144 | \t| | 145 | \t| 0 146 | \t| /-+-\\ 147 | \t| | 148 | \t| | 149 | \t| | | 150 | \t| | 151 | \t| 152 | \t-------- 153 | """, 154 | """ 155 | \t----- 156 | \t| | 157 | \t| 0 158 | \t| /-+-\\ 159 | \t| | 160 | \t| | 161 | \t| | | 162 | \t| | | 163 | \t| 164 | \t-------- 165 | """, 166 | ] 167 | 168 | 169 | class Hangman: 170 | def __init__(self): 171 | """ 172 | initializes a `Hangman` object 173 | """ 174 | 175 | pass 176 | 177 | def game(self): 178 | """ 179 | starts the game 180 | """ 181 | 182 | self.message = "" 183 | 184 | lives = 11 185 | 186 | word = [character.upper() for character in random.choice(WORDS)] 187 | word_hidden = ["_" for character in word] 188 | guessed = [] 189 | 190 | while (lives != 0) and ("_" in word_hidden): 191 | cls() 192 | 193 | print(HANGMAN[-lives]) 194 | print() 195 | 196 | if guessed: 197 | print(" ".join(guessed)) 198 | print() 199 | 200 | print("".join(word_hidden)) 201 | print() 202 | 203 | if self.message: 204 | print(self.message) 205 | print() 206 | 207 | self.message = "" 208 | else: 209 | print("you have {0} attempt{1} remaining".format(lives, "" if lives == 1 else "s")) 210 | print() 211 | 212 | letter = input("choose a letter;\n> ").upper() 213 | if not letter: 214 | continue 215 | elif not len(letter) == 1: 216 | self.message = "too many letters" 217 | continue 218 | elif not letter.isalpha(): 219 | self.message = "that is not a letter" 220 | continue 221 | elif letter in guessed: 222 | self.message = "you've already guessed that letter" 223 | continue 224 | 225 | guessed.append(letter) 226 | 227 | if letter not in word: 228 | lives -= 1 229 | continue 230 | 231 | for i in range(len(word)): 232 | if letter == word[i]: 233 | word_hidden[i] = letter 234 | 235 | cls() 236 | 237 | print(HANGMAN[-lives]) 238 | print() 239 | 240 | if guessed: 241 | print(" ".join(guessed)) 242 | print() 243 | 244 | print("".join(word_hidden)) 245 | print() 246 | 247 | if lives == 0: 248 | print("you lose") 249 | print("the word was {0}".format("".join(word))) 250 | else: 251 | print("you win") 252 | 253 | def start(self): 254 | """ 255 | calls `self.game` in a 'would you like to play again?' loop 256 | """ 257 | 258 | choice = "y" 259 | while choice.startswith("y"): 260 | cls() 261 | print(pyfiglet.figlet_format("Hangman")) 262 | print() 263 | input("enter to play\nctrl + c to quit to main menu\n\n") 264 | 265 | self.game() 266 | choice = input("\nwould you like to play again?\n> ").strip() 267 | 268 | 269 | WORDS = """aa 270 | ab 271 | ad 272 | ag 273 | ah 274 | ai 275 | am 276 | an 277 | as 278 | at 279 | aw 280 | ax 281 | ay 282 | ba 283 | be 284 | bi 285 | bo 286 | by 287 | da 288 | do 289 | dy 290 | ee 291 | eh 292 | el 293 | em 294 | en 295 | er 296 | ex 297 | fa 298 | ga 299 | gi 300 | go 301 | ha 302 | he 303 | hi 304 | ho 305 | id 306 | if 307 | in 308 | io 309 | is 310 | it 311 | ka 312 | ki 313 | la 314 | li 315 | lo 316 | ma 317 | me 318 | mo 319 | my 320 | no 321 | of 322 | oh 323 | oi 324 | ok 325 | on 326 | op 327 | or 328 | ou 329 | ow 330 | ox 331 | oz 332 | pa 333 | pi 334 | re 335 | so 336 | te 337 | to 338 | uh 339 | um 340 | up 341 | us 342 | wa 343 | we 344 | ye 345 | yo 346 | act 347 | add 348 | age 349 | ago 350 | aid 351 | aim 352 | air 353 | all 354 | and 355 | any 356 | arm 357 | art 358 | ask 359 | bad 360 | bag 361 | ban 362 | bar 363 | bay 364 | bed 365 | bet 366 | bid 367 | big 368 | bin 369 | bit 370 | box 371 | boy 372 | bus 373 | but 374 | buy 375 | bye 376 | can 377 | cap 378 | car 379 | cat 380 | cow 381 | cry 382 | cup 383 | cut 384 | dad 385 | day 386 | die 387 | dig 388 | dog 389 | dot 390 | dry 391 | due 392 | dvd 393 | dye 394 | ear 395 | eat 396 | egg 397 | end 398 | etc 399 | eye 400 | fan 401 | far 402 | fat 403 | fee 404 | few 405 | fit 406 | fix 407 | flu 408 | fly 409 | for 410 | fry 411 | fun 412 | fur 413 | gap 414 | gas 415 | get 416 | god 417 | gun 418 | guy 419 | hat 420 | her 421 | hip 422 | his 423 | hit 424 | hot 425 | how 426 | ice 427 | ill 428 | ink 429 | jam 430 | job 431 | joy 432 | key 433 | kid 434 | lab 435 | law 436 | lay 437 | leg 438 | let 439 | lid 440 | lie 441 | lip 442 | lot 443 | low 444 | mad 445 | map 446 | may 447 | mix 448 | mrs 449 | net 450 | new 451 | nor 452 | not 453 | now 454 | nut 455 | odd 456 | off 457 | oil 458 | old 459 | one 460 | our 461 | out 462 | owe 463 | own 464 | pan 465 | pay 466 | pen 467 | per 468 | pet 469 | pig 470 | pin 471 | pop 472 | pot 473 | pub 474 | put 475 | raw 476 | red 477 | rid 478 | rob 479 | rub 480 | run 481 | say 482 | sea 483 | see 484 | set 485 | sew 486 | sex 487 | she 488 | shy 489 | sir 490 | sit 491 | six 492 | sky 493 | son 494 | sum 495 | sun 496 | tap 497 | tax 498 | tea 499 | ten 500 | the 501 | tie 502 | tin 503 | tip 504 | toe 505 | ton 506 | too 507 | top 508 | toy 509 | try 510 | two 511 | use 512 | van 513 | via 514 | war 515 | way 516 | web 517 | wet 518 | who 519 | why 520 | win 521 | yes 522 | yet 523 | you 524 | able 525 | acid 526 | aged 527 | ally 528 | also 529 | area 530 | arms 531 | army 532 | atom 533 | aunt 534 | away 535 | baby 536 | back 537 | bake 538 | ball 539 | band 540 | bank 541 | base 542 | bath 543 | beak 544 | bear 545 | beat 546 | beef 547 | beer 548 | bell 549 | belt 550 | bend 551 | bent 552 | best 553 | bike 554 | bill 555 | bird 556 | bite 557 | blow 558 | blue 559 | boat 560 | body 561 | boil 562 | bomb 563 | bone 564 | book 565 | boot 566 | bore 567 | born 568 | boss 569 | both 570 | bowl 571 | burn 572 | bury 573 | bush 574 | busy 575 | cake 576 | call 577 | calm 578 | camp 579 | card 580 | care 581 | case 582 | cash 583 | cast 584 | cell 585 | cent 586 | chat 587 | chew 588 | chin 589 | chip 590 | chop 591 | city 592 | clap 593 | club 594 | coal 595 | coat 596 | code 597 | coin 598 | cold 599 | come 600 | cook 601 | cool 602 | cope 603 | copy 604 | core 605 | cost 606 | crop 607 | curb 608 | cure 609 | curl 610 | damp 611 | dare 612 | dark 613 | data 614 | date 615 | dead 616 | deaf 617 | deal 618 | dear 619 | debt 620 | deep 621 | deny 622 | desk 623 | diet 624 | dirt 625 | disc 626 | dish 627 | disk 628 | door 629 | down 630 | drag 631 | draw 632 | drop 633 | drug 634 | drum 635 | dull 636 | dump 637 | dust 638 | duty 639 | each 640 | earn 641 | ease 642 | east 643 | easy 644 | edge 645 | else 646 | euro 647 | even 648 | ever 649 | evil 650 | exam 651 | exit 652 | face 653 | fact 654 | fail 655 | fair 656 | fall 657 | fame 658 | farm 659 | fast 660 | fear 661 | feed 662 | feel 663 | file 664 | fill 665 | film 666 | find 667 | fine 668 | fire 669 | firm 670 | fish 671 | five 672 | flag 673 | flat 674 | flow 675 | fold 676 | food 677 | foot 678 | fork 679 | form 680 | four 681 | free 682 | from 683 | fuel 684 | full 685 | fund 686 | gain 687 | game 688 | gate 689 | gear 690 | gift 691 | girl 692 | give 693 | glad 694 | glue 695 | goal 696 | gold 697 | good 698 | grab 699 | gram 700 | grey 701 | grow 702 | hair 703 | half 704 | hall 705 | hand 706 | hang 707 | hard 708 | harm 709 | hate 710 | have 711 | head 712 | heal 713 | hear 714 | heat 715 | heel 716 | hell 717 | help 718 | here 719 | hero 720 | hers 721 | hide 722 | high 723 | hill 724 | hire 725 | hold 726 | hole 727 | holy 728 | home 729 | hook 730 | hope 731 | horn 732 | host 733 | hour 734 | huge 735 | hunt 736 | hurt 737 | idea 738 | inch 739 | into 740 | iron 741 | item 742 | join 743 | joke 744 | july 745 | jump 746 | june 747 | just 748 | keen 749 | keep 750 | kick 751 | kill 752 | kind 753 | king 754 | kiss 755 | knee 756 | knit 757 | knot 758 | know 759 | lack 760 | lady 761 | lake 762 | lamp 763 | land 764 | lane 765 | last 766 | late 767 | lazy 768 | lead 769 | leaf 770 | lean 771 | left 772 | lend 773 | less 774 | life 775 | lift 776 | like 777 | line 778 | link 779 | list 780 | live 781 | load 782 | loan 783 | lock 784 | long 785 | look 786 | lord 787 | lose 788 | loss 789 | lost 790 | loud 791 | love 792 | luck 793 | lump 794 | lung 795 | mail 796 | main 797 | make 798 | male 799 | mall 800 | many 801 | mark 802 | mass 803 | mate 804 | meal 805 | mean 806 | meat 807 | meet 808 | melt 809 | menu 810 | mere 811 | mess 812 | mild 813 | mile 814 | milk 815 | mind 816 | mine 817 | miss 818 | mood 819 | moon 820 | more 821 | most 822 | move 823 | much 824 | must 825 | nail 826 | name 827 | navy 828 | near 829 | neat 830 | neck 831 | need 832 | nest 833 | news 834 | next 835 | nice 836 | nine 837 | none 838 | nose 839 | note 840 | obey 841 | once 842 | only 843 | onto 844 | open 845 | ours 846 | oven 847 | over 848 | pace 849 | pack 850 | page 851 | pain 852 | pair 853 | pale 854 | park 855 | part 856 | pass 857 | past 858 | path 859 | peak 860 | pick 861 | pile 862 | pill 863 | pink 864 | pint 865 | pipe 866 | pity 867 | plan 868 | play 869 | plot 870 | plug 871 | plus 872 | poem 873 | pole 874 | pool 875 | poor 876 | port 877 | pose 878 | post 879 | pour 880 | pray 881 | pull 882 | pure 883 | push 884 | quit 885 | race 886 | rail 887 | rain 888 | rank 889 | rare 890 | rate 891 | read 892 | real 893 | rear 894 | rely 895 | rent 896 | rest 897 | rice 898 | rich 899 | ride 900 | ring 901 | rise 902 | risk 903 | road 904 | rock 905 | role 906 | roll 907 | roof 908 | room 909 | root 910 | rope 911 | rude 912 | ruin 913 | rule 914 | rush 915 | sack 916 | safe 917 | sail 918 | sale 919 | salt 920 | same 921 | sand 922 | save 923 | seal 924 | seat 925 | seed 926 | seek 927 | seem 928 | self 929 | sell 930 | send 931 | ship 932 | shoe 933 | shop 934 | shot 935 | show 936 | shut 937 | sick 938 | side 939 | sign 940 | silk 941 | sing 942 | sink 943 | site 944 | size 945 | skin 946 | slip 947 | slow 948 | snow 949 | soap 950 | sock 951 | soft 952 | soil 953 | some 954 | song 955 | soon 956 | sore 957 | sort 958 | soul 959 | soup 960 | sour 961 | spin 962 | spot 963 | star 964 | stay 965 | step 966 | stir 967 | stop 968 | such 969 | suck 970 | suit 971 | sure 972 | swim 973 | tail 974 | take 975 | talk 976 | tall 977 | tank 978 | tape 979 | task 980 | taxi 981 | team 982 | tear 983 | tell 984 | tend 985 | tent 986 | term 987 | test 988 | text 989 | than 990 | that 991 | them 992 | then 993 | they 994 | thin 995 | this 996 | thus 997 | tidy 998 | till 999 | time 1000 | tiny 1001 | tire 1002 | tone 1003 | tool 1004 | tour 1005 | town 1006 | trap 1007 | tree 1008 | trip 1009 | true 1010 | tube 1011 | tune 1012 | turn 1013 | twin 1014 | type 1015 | tyre 1016 | ugly 1017 | undo 1018 | unit 1019 | upon 1020 | urge 1021 | used 1022 | user 1023 | vary 1024 | vast 1025 | very 1026 | view 1027 | vote 1028 | wage 1029 | wait 1030 | wake 1031 | walk 1032 | wall 1033 | want 1034 | warm 1035 | warn 1036 | wash 1037 | wave 1038 | weak 1039 | wear 1040 | week 1041 | well 1042 | west 1043 | what 1044 | when 1045 | whom 1046 | wide 1047 | wife 1048 | wild 1049 | will 1050 | wind 1051 | wine 1052 | wing 1053 | wire 1054 | wise 1055 | wish 1056 | with 1057 | wood 1058 | wool 1059 | word 1060 | work 1061 | wrap 1062 | zero 1063 | zone 1064 | about 1065 | above 1066 | abuse 1067 | actor 1068 | adapt 1069 | admit 1070 | adopt 1071 | adult 1072 | after 1073 | again 1074 | agent 1075 | agree 1076 | ahead 1077 | alarm 1078 | alive 1079 | allow 1080 | alone 1081 | along 1082 | aloud 1083 | alter 1084 | amaze 1085 | among 1086 | amuse 1087 | anger 1088 | angle 1089 | angry 1090 | ankle 1091 | annoy 1092 | apart 1093 | apple 1094 | apply 1095 | april 1096 | argue 1097 | arise 1098 | armed 1099 | arrow 1100 | aside 1101 | avoid 1102 | awake 1103 | award 1104 | aware 1105 | awful 1106 | badly 1107 | based 1108 | basic 1109 | basis 1110 | beach 1111 | beard 1112 | begin 1113 | below 1114 | birth 1115 | black 1116 | blade 1117 | blame 1118 | blank 1119 | blind 1120 | block 1121 | blood 1122 | board 1123 | bored 1124 | bound 1125 | brain 1126 | brand 1127 | brave 1128 | bread 1129 | break 1130 | breed 1131 | brick 1132 | brief 1133 | bring 1134 | broad 1135 | brown 1136 | brush 1137 | build 1138 | bunch 1139 | burnt 1140 | burst 1141 | buyer 1142 | cable 1143 | candy 1144 | carry 1145 | catch 1146 | cause 1147 | cease 1148 | chain 1149 | chair 1150 | chart 1151 | chase 1152 | cheap 1153 | cheat 1154 | check 1155 | cheek 1156 | chest 1157 | chief 1158 | child 1159 | civil 1160 | claim 1161 | class 1162 | clean 1163 | clear 1164 | clerk 1165 | click 1166 | climb 1167 | clock 1168 | close 1169 | cloth 1170 | cloud 1171 | coach 1172 | coast 1173 | cough 1174 | could 1175 | count 1176 | court 1177 | cover 1178 | crack 1179 | craft 1180 | crash 1181 | crazy 1182 | cream 1183 | crime 1184 | crisp 1185 | cross 1186 | crowd 1187 | crown 1188 | cruel 1189 | crush 1190 | curly 1191 | curve 1192 | cycle 1193 | daily 1194 | dance 1195 | death 1196 | decay 1197 | delay 1198 | depth 1199 | diary 1200 | dirty 1201 | doubt 1202 | dozen 1203 | draft 1204 | drama 1205 | dream 1206 | dress 1207 | drink 1208 | drive 1209 | drunk 1210 | dying 1211 | early 1212 | earth 1213 | eight 1214 | elbow 1215 | elect 1216 | email 1217 | empty 1218 | enemy 1219 | enjoy 1220 | enter 1221 | entry 1222 | equal 1223 | error 1224 | essay 1225 | event 1226 | every 1227 | exact 1228 | exist 1229 | extra 1230 | faint 1231 | faith 1232 | false 1233 | fancy 1234 | fault 1235 | fence 1236 | fetch 1237 | fever 1238 | field 1239 | fifth 1240 | fifty 1241 | fight 1242 | final 1243 | first 1244 | fixed 1245 | flame 1246 | flash 1247 | flesh 1248 | float 1249 | flood 1250 | floor 1251 | flour 1252 | focus 1253 | force 1254 | forty 1255 | found 1256 | frame 1257 | fresh 1258 | front 1259 | fruit 1260 | fully 1261 | funny 1262 | giant 1263 | glass 1264 | glove 1265 | goods 1266 | grade 1267 | grain 1268 | grand 1269 | grant 1270 | grass 1271 | grave 1272 | great 1273 | green 1274 | group 1275 | guard 1276 | guess 1277 | guest 1278 | guide 1279 | habit 1280 | happy 1281 | heart 1282 | heavy 1283 | hello 1284 | hence 1285 | hobby 1286 | horse 1287 | hotel 1288 | house 1289 | human 1290 | hurry 1291 | ideal 1292 | image 1293 | imply 1294 | index 1295 | inner 1296 | issue 1297 | jeans 1298 | jelly 1299 | joint 1300 | judge 1301 | juice 1302 | knife 1303 | knock 1304 | label 1305 | large 1306 | later 1307 | laugh 1308 | layer 1309 | learn 1310 | least 1311 | leave 1312 | legal 1313 | lemon 1314 | level 1315 | light 1316 | limit 1317 | litre 1318 | local 1319 | logic 1320 | loose 1321 | lorry 1322 | lover 1323 | loyal 1324 | lucky 1325 | lunch 1326 | magic 1327 | major 1328 | march 1329 | marry 1330 | match 1331 | maybe 1332 | mayor 1333 | means 1334 | media 1335 | metal 1336 | metre 1337 | might 1338 | minor 1339 | mixed 1340 | model 1341 | money 1342 | month 1343 | moral 1344 | motor 1345 | mount 1346 | mouse 1347 | mouth 1348 | movie 1349 | music 1350 | naked 1351 | nerve 1352 | never 1353 | newly 1354 | niece 1355 | night 1356 | ninth 1357 | noise 1358 | noisy 1359 | north 1360 | novel 1361 | nurse 1362 | occur 1363 | ocean 1364 | oddly 1365 | offer 1366 | often 1367 | onion 1368 | order 1369 | organ 1370 | other 1371 | ought 1372 | outer 1373 | owner 1374 | paint 1375 | panel 1376 | pants 1377 | paper 1378 | party 1379 | pause 1380 | peace 1381 | penny 1382 | phase 1383 | phone 1384 | photo 1385 | piano 1386 | piece 1387 | pilot 1388 | pitch 1389 | place 1390 | plain 1391 | plane 1392 | plant 1393 | plate 1394 | point 1395 | pound 1396 | power 1397 | press 1398 | price 1399 | pride 1400 | prime 1401 | print 1402 | prior 1403 | prize 1404 | proof 1405 | proud 1406 | prove 1407 | punch 1408 | pupil 1409 | queen 1410 | quick 1411 | quiet 1412 | quite 1413 | quote 1414 | radio 1415 | raise 1416 | range 1417 | rapid 1418 | reach 1419 | react 1420 | ready 1421 | refer 1422 | relax 1423 | reply 1424 | rider 1425 | right 1426 | rival 1427 | river 1428 | rough 1429 | round 1430 | route 1431 | royal 1432 | ruler 1433 | rural 1434 | sadly 1435 | salad 1436 | salty 1437 | sauce 1438 | scale 1439 | scare 1440 | scene 1441 | score 1442 | screw 1443 | sense 1444 | serve 1445 | seven 1446 | shade 1447 | shake 1448 | shall 1449 | shame 1450 | shape 1451 | share 1452 | sharp 1453 | shave 1454 | sheep 1455 | sheet 1456 | shelf 1457 | shell 1458 | shift 1459 | shine 1460 | shiny 1461 | shirt 1462 | shock 1463 | shoot 1464 | short 1465 | shout 1466 | sight 1467 | silly 1468 | since 1469 | sixth 1470 | sixty 1471 | skill 1472 | skirt 1473 | sleep 1474 | slice 1475 | slide 1476 | slope 1477 | small 1478 | smart 1479 | smash 1480 | smell 1481 | smile 1482 | smoke 1483 | snake 1484 | solid 1485 | solve 1486 | sorry 1487 | sound 1488 | south 1489 | space 1490 | spare 1491 | speak 1492 | speed 1493 | spell 1494 | spend 1495 | spice 1496 | spicy 1497 | spite 1498 | split 1499 | spoil 1500 | spoon 1501 | sport 1502 | spray 1503 | staff 1504 | stage 1505 | stair 1506 | stamp 1507 | stand 1508 | stare 1509 | start 1510 | state 1511 | steal 1512 | steam 1513 | steel 1514 | steep 1515 | steer 1516 | stick 1517 | stiff 1518 | still 1519 | sting 1520 | stock 1521 | stone 1522 | store 1523 | storm 1524 | story 1525 | stove 1526 | strip 1527 | study 1528 | stuff 1529 | style 1530 | sugar 1531 | swear 1532 | sweat 1533 | sweep 1534 | sweet 1535 | swell 1536 | swing 1537 | table 1538 | taste 1539 | teach 1540 | tenth 1541 | thank 1542 | their 1543 | theme 1544 | there 1545 | thick 1546 | thief 1547 | thing 1548 | think 1549 | third 1550 | three 1551 | throw 1552 | thumb 1553 | tight 1554 | tired 1555 | title 1556 | today 1557 | tonne 1558 | tooth 1559 | topic 1560 | total 1561 | touch 1562 | tough 1563 | towel 1564 | tower 1565 | trace 1566 | track 1567 | trade 1568 | train 1569 | treat 1570 | trend 1571 | trial 1572 | trick 1573 | truck 1574 | truly 1575 | trust 1576 | truth 1577 | twice 1578 | twist 1579 | uncle 1580 | under 1581 | union 1582 | unite 1583 | until 1584 | upper 1585 | upset 1586 | urban 1587 | usual 1588 | valid 1589 | value 1590 | video 1591 | virus 1592 | visit 1593 | vital 1594 | voice 1595 | waist 1596 | waste 1597 | watch 1598 | water 1599 | weigh 1600 | wheel 1601 | where 1602 | which 1603 | while 1604 | white 1605 | whole 1606 | whose 1607 | width 1608 | woman 1609 | world 1610 | worry 1611 | worse 1612 | worst 1613 | worth 1614 | would 1615 | wound 1616 | wrist 1617 | write 1618 | wrong 1619 | young 1620 | yours 1621 | youth 1622 | abroad 1623 | absent 1624 | absorb 1625 | accent 1626 | accept 1627 | access 1628 | accuse 1629 | across 1630 | action 1631 | active 1632 | actual 1633 | adjust 1634 | admire 1635 | advert 1636 | advice 1637 | advise 1638 | affair 1639 | affect 1640 | afford 1641 | afraid 1642 | agency 1643 | allied 1644 | almost 1645 | always 1646 | amazed 1647 | amount 1648 | amused 1649 | animal 1650 | annual 1651 | answer 1652 | anyone 1653 | anyway 1654 | appeal 1655 | appear 1656 | around 1657 | arrest 1658 | arrive 1659 | artist 1660 | asleep 1661 | aspect 1662 | assist 1663 | assume 1664 | assure 1665 | attach 1666 | attack 1667 | attend 1668 | august 1669 | author 1670 | autumn 1671 | battle 1672 | beauty 1673 | become 1674 | before 1675 | behalf 1676 | behave 1677 | behind 1678 | belief 1679 | belong 1680 | beside 1681 | better 1682 | beyond 1683 | bitter 1684 | blonde 1685 | border 1686 | boring 1687 | borrow 1688 | bother 1689 | bottle 1690 | bottom 1691 | branch 1692 | breast 1693 | breath 1694 | bridge 1695 | bright 1696 | broken 1697 | bubble 1698 | budget 1699 | bullet 1700 | butter 1701 | button 1702 | called 1703 | calmly 1704 | camera 1705 | cancel 1706 | cancer 1707 | cannot 1708 | career 1709 | carpet 1710 | carrot 1711 | castle 1712 | centre 1713 | chance 1714 | change 1715 | charge 1716 | cheese 1717 | cheque 1718 | choice 1719 | choose 1720 | church 1721 | cinema 1722 | circle 1723 | clever 1724 | client 1725 | closed 1726 | closet 1727 | coffee 1728 | coldly 1729 | colour 1730 | column 1731 | comedy 1732 | commit 1733 | common 1734 | cooker 1735 | cookie 1736 | corner 1737 | cotton 1738 | county 1739 | couple 1740 | course 1741 | cousin 1742 | create 1743 | credit 1744 | crisis 1745 | curved 1746 | custom 1747 | damage 1748 | dancer 1749 | danger 1750 | debate 1751 | decade 1752 | decide 1753 | deeply 1754 | defeat 1755 | defend 1756 | define 1757 | degree 1758 | demand 1759 | depend 1760 | derive 1761 | desert 1762 | design 1763 | desire 1764 | detail 1765 | device 1766 | devote 1767 | dinner 1768 | direct 1769 | divide 1770 | doctor 1771 | dollar 1772 | double 1773 | drawer 1774 | driver 1775 | during 1776 | easily 1777 | editor 1778 | effect 1779 | effort 1780 | eighth 1781 | eighty 1782 | either 1783 | eleven 1784 | emerge 1785 | empire 1786 | employ 1787 | enable 1788 | ending 1789 | energy 1790 | engage 1791 | engine 1792 | enough 1793 | ensure 1794 | entire 1795 | escape 1796 | estate 1797 | except 1798 | excite 1799 | excuse 1800 | expand 1801 | expect 1802 | expert 1803 | export 1804 | expose 1805 | extend 1806 | extent 1807 | factor 1808 | fairly 1809 | family 1810 | famous 1811 | farmer 1812 | fasten 1813 | father 1814 | faucet 1815 | favour 1816 | fellow 1817 | female 1818 | figure 1819 | finely 1820 | finger 1821 | finish 1822 | firmly 1823 | flight 1824 | flower 1825 | flying 1826 | follow 1827 | forest 1828 | forget 1829 | formal 1830 | former 1831 | fourth 1832 | freely 1833 | freeze 1834 | friday 1835 | fridge 1836 | friend 1837 | frozen 1838 | future 1839 | gallon 1840 | gamble 1841 | garage 1842 | garden 1843 | gather 1844 | gentle 1845 | gently 1846 | global 1847 | govern 1848 | ground 1849 | growth 1850 | guilty 1851 | hammer 1852 | handle 1853 | happen 1854 | hardly 1855 | hatred 1856 | health 1857 | heaven 1858 | height 1859 | highly 1860 | hollow 1861 | honest 1862 | honour 1863 | horror 1864 | humour 1865 | hungry 1866 | ignore 1867 | impact 1868 | import 1869 | impose 1870 | income 1871 | indeed 1872 | indoor 1873 | infect 1874 | inform 1875 | injure 1876 | injury 1877 | insect 1878 | insert 1879 | inside 1880 | insist 1881 | insult 1882 | intend 1883 | invent 1884 | invest 1885 | invite 1886 | island 1887 | itself 1888 | jacket 1889 | junior 1890 | kindly 1891 | labour 1892 | latest 1893 | latter 1894 | launch 1895 | lawyer 1896 | leader 1897 | league 1898 | length 1899 | lesson 1900 | letter 1901 | likely 1902 | liquid 1903 | listen 1904 | little 1905 | lively 1906 | living 1907 | locate 1908 | lonely 1909 | loudly 1910 | lovely 1911 | mainly 1912 | manage 1913 | manner 1914 | market 1915 | master 1916 | matter 1917 | medium 1918 | member 1919 | memory 1920 | mental 1921 | merely 1922 | method 1923 | midday 1924 | middle 1925 | minute 1926 | mirror 1927 | mobile 1928 | modern 1929 | moment 1930 | monday 1931 | mostly 1932 | mother 1933 | motion 1934 | moving 1935 | murder 1936 | muscle 1937 | museum 1938 | myself 1939 | narrow 1940 | nation 1941 | nature 1942 | nearby 1943 | nearly 1944 | neatly 1945 | needle 1946 | nephew 1947 | nicely 1948 | ninety 1949 | nobody 1950 | normal 1951 | notice 1952 | number 1953 | object 1954 | obtain 1955 | occupy 1956 | offend 1957 | office 1958 | online 1959 | openly 1960 | oppose 1961 | option 1962 | orange 1963 | origin 1964 | output 1965 | packet 1966 | palace 1967 | parent 1968 | partly 1969 | pencil 1970 | people 1971 | pepper 1972 | period 1973 | permit 1974 | person 1975 | petrol 1976 | phrase 1977 | planet 1978 | player 1979 | please 1980 | plenty 1981 | pocket 1982 | poetry 1983 | poison 1984 | police 1985 | policy 1986 | polish 1987 | polite 1988 | potato 1989 | powder 1990 | praise 1991 | prayer 1992 | prefer 1993 | pretty 1994 | priest 1995 | prince 1996 | prison 1997 | profit 1998 | prompt 1999 | proper 2000 | public 2001 | punish 2002 | purely 2003 | purple 2004 | pursue 2005 | racing 2006 | rarely 2007 | rather 2008 | reader 2009 | really 2010 | reason 2011 | recall 2012 | recent 2013 | reckon 2014 | record 2015 | reduce 2016 | reform 2017 | refuse 2018 | regard 2019 | region 2020 | regret 2021 | reject 2022 | relate 2023 | relief 2024 | remain 2025 | remark 2026 | remind 2027 | remote 2028 | remove 2029 | rented 2030 | repair 2031 | repeat 2032 | report 2033 | rescue 2034 | resist 2035 | resort 2036 | result 2037 | retain 2038 | retire 2039 | return 2040 | reveal 2041 | review 2042 | revise 2043 | reward 2044 | rhythm 2045 | riding 2046 | rubber 2047 | rudely 2048 | ruined 2049 | rumour 2050 | runner 2051 | safely 2052 | safety 2053 | sailor 2054 | salary 2055 | sample 2056 | saving 2057 | scared 2058 | scheme 2059 | school 2060 | scream 2061 | screen 2062 | search 2063 | season 2064 | second 2065 | secret 2066 | sector 2067 | secure 2068 | select 2069 | senate 2070 | senior 2071 | series 2072 | settle 2073 | severe 2074 | sewing 2075 | sexual 2076 | shadow 2077 | shaped 2078 | should 2079 | shower 2080 | signal 2081 | silent 2082 | silver 2083 | simple 2084 | simply 2085 | singer 2086 | single 2087 | sister 2088 | sleeve 2089 | slight 2090 | slowly 2091 | smooth 2092 | social 2093 | softly 2094 | source 2095 | speech 2096 | spider 2097 | spirit 2098 | spoken 2099 | spread 2100 | spring 2101 | square 2102 | stable 2103 | statue 2104 | status 2105 | steady 2106 | sticky 2107 | strain 2108 | stream 2109 | street 2110 | stress 2111 | strict 2112 | strike 2113 | string 2114 | stripe 2115 | stroke 2116 | strong 2117 | studio 2118 | stupid 2119 | sudden 2120 | suffer 2121 | suited 2122 | summer 2123 | sunday 2124 | supply 2125 | surely 2126 | survey 2127 | switch 2128 | symbol 2129 | system 2130 | tablet 2131 | tackle 2132 | target 2133 | thanks 2134 | theirs 2135 | theory 2136 | thirty 2137 | though 2138 | thread 2139 | threat 2140 | throat 2141 | ticket 2142 | tiring 2143 | toilet 2144 | tomato 2145 | tongue 2146 | travel 2147 | tunnel 2148 | twelve 2149 | twenty 2150 | unable 2151 | unfair 2152 | unique 2153 | united 2154 | unkind 2155 | unless 2156 | unlike 2157 | unload 2158 | untidy 2159 | upside 2160 | upward 2161 | urgent 2162 | useful 2163 | valley 2164 | varied 2165 | victim 2166 | vision 2167 | volume 2168 | waiter 2169 | wallet 2170 | wander 2171 | warmth 2172 | wealth 2173 | weapon 2174 | weekly 2175 | weight 2176 | widely 2177 | wildly 2178 | window 2179 | winner 2180 | winter 2181 | within 2182 | wonder 2183 | wooden 2184 | worker 2185 | writer 2186 | yellow 2187 | abandon 2188 | ability 2189 | absence 2190 | account 2191 | achieve 2192 | acquire 2193 | actress 2194 | address 2195 | advance 2196 | against 2197 | airport 2198 | alarmed 2199 | alcohol 2200 | already 2201 | amazing 2202 | amusing 2203 | analyse 2204 | ancient 2205 | angrily 2206 | annoyed 2207 | another 2208 | anxiety 2209 | anxious 2210 | anybody 2211 | appoint 2212 | approve 2213 | arrange 2214 | arrival 2215 | article 2216 | ashamed 2217 | attempt 2218 | attract 2219 | average 2220 | awfully 2221 | awkward 2222 | baggage 2223 | balance 2224 | bandage 2225 | bargain 2226 | barrier 2227 | battery 2228 | because 2229 | bedroom 2230 | believe 2231 | beneath 2232 | benefit 2233 | betting 2234 | between 2235 | bicycle 2236 | billion 2237 | biology 2238 | biscuit 2239 | breathe 2240 | briefly 2241 | broadly 2242 | brother 2243 | cabinet 2244 | camping 2245 | capable 2246 | capital 2247 | captain 2248 | capture 2249 | careful 2250 | ceiling 2251 | central 2252 | century 2253 | certain 2254 | chamber 2255 | channel 2256 | chapter 2257 | charity 2258 | cheaply 2259 | chemist 2260 | chicken 2261 | citizen 2262 | classic 2263 | clearly 2264 | climate 2265 | closely 2266 | clothes 2267 | collect 2268 | college 2269 | combine 2270 | comfort 2271 | command 2272 | comment 2273 | company 2274 | compare 2275 | compete 2276 | complex 2277 | concept 2278 | concern 2279 | concert 2280 | conduct 2281 | confine 2282 | confirm 2283 | confuse 2284 | connect 2285 | consist 2286 | consult 2287 | contact 2288 | contain 2289 | content 2290 | contest 2291 | context 2292 | control 2293 | convert 2294 | cooking 2295 | correct 2296 | cottage 2297 | council 2298 | counter 2299 | country 2300 | courage 2301 | covered 2302 | cracked 2303 | crowded 2304 | crucial 2305 | culture 2306 | curious 2307 | current 2308 | curtain 2309 | customs 2310 | cycling 2311 | dancing 2312 | declare 2313 | decline 2314 | defence 2315 | delight 2316 | deliver 2317 | dentist 2318 | deposit 2319 | depress 2320 | deserve 2321 | despite 2322 | destroy 2323 | develop 2324 | devoted 2325 | diagram 2326 | diamond 2327 | digital 2328 | discuss 2329 | disease 2330 | disgust 2331 | dislike 2332 | dismiss 2333 | display 2334 | disturb 2335 | divorce 2336 | drawing 2337 | dressed 2338 | driving 2339 | eastern 2340 | economy 2341 | edition 2342 | educate 2343 | elderly 2344 | elegant 2345 | element 2346 | emotion 2347 | engaged 2348 | enquiry 2349 | entitle 2350 | equally 2351 | evening 2352 | exactly 2353 | examine 2354 | example 2355 | excited 2356 | exclude 2357 | exhibit 2358 | expense 2359 | explain 2360 | explode 2361 | explore 2362 | express 2363 | extreme 2364 | factory 2365 | failure 2366 | faintly 2367 | farming 2368 | farther 2369 | fashion 2370 | feather 2371 | feature 2372 | federal 2373 | feeling 2374 | fifteen 2375 | finally 2376 | finance 2377 | fishing 2378 | flavour 2379 | flooded 2380 | folding 2381 | foreign 2382 | forever 2383 | forgive 2384 | formula 2385 | fortune 2386 | forward 2387 | freedom 2388 | freshly 2389 | funeral 2390 | further 2391 | garbage 2392 | general 2393 | genuine 2394 | goodbye 2395 | gradual 2396 | grammar 2397 | gravely 2398 | greatly 2399 | grocery 2400 | happily 2401 | harmful 2402 | healthy 2403 | hearing 2404 | heating 2405 | heavily 2406 | helpful 2407 | herself 2408 | highway 2409 | himself 2410 | history 2411 | holiday 2412 | housing 2413 | however 2414 | hundred 2415 | hunting 2416 | husband 2417 | illegal 2418 | illness 2419 | imagine 2420 | immoral 2421 | impress 2422 | improve 2423 | include 2424 | indoors 2425 | initial 2426 | injured 2427 | install 2428 | instead 2429 | involve 2430 | january 2431 | jealous 2432 | jointly 2433 | journey 2434 | justice 2435 | justify 2436 | killing 2437 | kitchen 2438 | knitted 2439 | lacking 2440 | largely 2441 | leading 2442 | leather 2443 | lecture 2444 | legally 2445 | library 2446 | licence 2447 | license 2448 | lightly 2449 | limited 2450 | locally 2451 | located 2452 | logical 2453 | loosely 2454 | luggage 2455 | machine 2456 | manager 2457 | married 2458 | massive 2459 | maximum 2460 | meaning 2461 | measure 2462 | medical 2463 | meeting 2464 | mention 2465 | message 2466 | million 2467 | mineral 2468 | minimum 2469 | missing 2470 | mistake 2471 | mixture 2472 | monitor 2473 | morally 2474 | morning 2475 | musical 2476 | mystery 2477 | natural 2478 | neither 2479 | nervous 2480 | network 2481 | noisily 2482 | nothing 2483 | nowhere 2484 | nuclear 2485 | observe 2486 | obvious 2487 | october 2488 | offence 2489 | offense 2490 | officer 2491 | opening 2492 | operate 2493 | opinion 2494 | opposed 2495 | outdoor 2496 | outline 2497 | outside 2498 | overall 2499 | package 2500 | painful 2501 | painter 2502 | partner 2503 | passage 2504 | passing 2505 | patient 2506 | pattern 2507 | payment 2508 | pension 2509 | perfect 2510 | perform 2511 | perhaps 2512 | physics 2513 | picture 2514 | plastic 2515 | pleased 2516 | pointed 2517 | popular 2518 | possess 2519 | precise 2520 | predict 2521 | prepare 2522 | present 2523 | pretend 2524 | prevent 2525 | primary 2526 | printer 2527 | private 2528 | problem 2529 | proceed 2530 | process 2531 | produce 2532 | product 2533 | program 2534 | project 2535 | promise 2536 | promote 2537 | propose 2538 | protect 2539 | protest 2540 | proudly 2541 | provide 2542 | publish 2543 | purpose 2544 | qualify 2545 | quality 2546 | quarter 2547 | quickly 2548 | quietly 2549 | railway 2550 | rapidly 2551 | reading 2552 | reality 2553 | realize 2554 | receipt 2555 | receive 2556 | recover 2557 | reflect 2558 | refusal 2559 | regular 2560 | related 2561 | relaxed 2562 | release 2563 | remains 2564 | removal 2565 | replace 2566 | request 2567 | require 2568 | reserve 2569 | resolve 2570 | respect 2571 | respond 2572 | restore 2573 | retired 2574 | reverse 2575 | rightly 2576 | roughly 2577 | rounded 2578 | routine 2579 | rubbish 2580 | running 2581 | sadness 2582 | sailing 2583 | satisfy 2584 | science 2585 | scratch 2586 | section 2587 | senator 2588 | serious 2589 | servant 2590 | service 2591 | session 2592 | seventh 2593 | seventy 2594 | several 2595 | shallow 2596 | sharply 2597 | shelter 2598 | shocked 2599 | shortly 2600 | silence 2601 | similar 2602 | sincere 2603 | singing 2604 | sixteen 2605 | skilful 2606 | skilled 2607 | smoking 2608 | society 2609 | soldier 2610 | somehow 2611 | someone 2612 | speaker 2613 | special 2614 | squeeze 2615 | station 2616 | steeply 2617 | stiffly 2618 | stomach 2619 | strange 2620 | stretch 2621 | striped 2622 | student 2623 | subject 2624 | succeed 2625 | success 2626 | suggest 2627 | summary 2628 | support 2629 | suppose 2630 | surface 2631 | surname 2632 | survive 2633 | suspect 2634 | swallow 2635 | sweater 2636 | swollen 2637 | teacher 2638 | tension 2639 | theatre 2640 | thickly 2641 | thirsty 2642 | thought 2643 | through 2644 | tightly 2645 | tonight 2646 | totally 2647 | tourist 2648 | towards 2649 | trading 2650 | traffic 2651 | trouble 2652 | tuesday 2653 | twelfth 2654 | twisted 2655 | typical 2656 | unhappy 2657 | uniform 2658 | unknown 2659 | unlucky 2660 | unusual 2661 | upwards 2662 | useless 2663 | usually 2664 | variety 2665 | various 2666 | vehicle 2667 | venture 2668 | version 2669 | victory 2670 | village 2671 | violent 2672 | visible 2673 | visitor 2674 | walking 2675 | warning 2676 | washing 2677 | weather 2678 | website 2679 | wedding 2680 | weekend 2681 | welcome 2682 | western 2683 | whereas 2684 | whether 2685 | whisper 2686 | whistle 2687 | whoever 2688 | willing 2689 | winning 2690 | without 2691 | witness 2692 | working 2693 | worried 2694 | worship 2695 | wounded 2696 | writing 2697 | written 2698 | wrongly 2699 | absolute 2700 | academic 2701 | accident 2702 | accurate 2703 | actively 2704 | activity 2705 | actually 2706 | addition 2707 | adequate 2708 | advanced 2709 | aircraft 2710 | alarming 2711 | alphabet 2712 | although 2713 | ambition 2714 | analysis 2715 | announce 2716 | annoying 2717 | annually 2718 | anything 2719 | anywhere 2720 | apparent 2721 | approach 2722 | approval 2723 | argument 2724 | artistic 2725 | attached 2726 | attitude 2727 | attorney 2728 | audience 2729 | backward 2730 | bacteria 2731 | bathroom 2732 | birthday 2733 | bitterly 2734 | brightly 2735 | building 2736 | business 2737 | campaign 2738 | capacity 2739 | careless 2740 | category 2741 | ceremony 2742 | chairman 2743 | cheerful 2744 | chemical 2745 | climbing 2746 | clothing 2747 | collapse 2748 | coloured 2749 | commonly 2750 | complain 2751 | complete 2752 | computer 2753 | conclude 2754 | concrete 2755 | confined 2756 | conflict 2757 | confront 2758 | confused 2759 | congress 2760 | consider 2761 | constant 2762 | consumer 2763 | continue 2764 | contract 2765 | contrast 2766 | convince 2767 | coughing 2768 | covering 2769 | creature 2770 | criminal 2771 | critical 2772 | cultural 2773 | cupboard 2774 | customer 2775 | daughter 2776 | december 2777 | decision 2778 | decorate 2779 | decrease 2780 | definite 2781 | delicate 2782 | delivery 2783 | describe 2784 | deserted 2785 | detailed 2786 | directly 2787 | director 2788 | disabled 2789 | disagree 2790 | disaster 2791 | discount 2792 | discover 2793 | dissolve 2794 | distance 2795 | district 2796 | division 2797 | divorced 2798 | document 2799 | domestic 2800 | dominate 2801 | downward 2802 | dramatic 2803 | economic 2804 | educated 2805 | eighteen 2806 | election 2807 | electric 2808 | elevator 2809 | eleventh 2810 | emphasis 2811 | employee 2812 | employer 2813 | engineer 2814 | enormous 2815 | entirely 2816 | entrance 2817 | envelope 2818 | estimate 2819 | everyone 2820 | evidence 2821 | exchange 2822 | exciting 2823 | exercise 2824 | expected 2825 | facility 2826 | faithful 2827 | familiar 2828 | farthest 2829 | february 2830 | festival 2831 | fiftieth 2832 | fighting 2833 | finished 2834 | flooding 2835 | football 2836 | forecast 2837 | formally 2838 | formerly 2839 | fortieth 2840 | fourteen 2841 | frequent 2842 | friendly 2843 | frighten 2844 | function 2845 | gambling 2846 | gasoline 2847 | generate 2848 | generous 2849 | governor 2850 | grandson 2851 | grateful 2852 | harmless 2853 | headache 2854 | hesitate 2855 | homework 2856 | honestly 2857 | hospital 2858 | humorous 2859 | identify 2860 | identity 2861 | incident 2862 | increase 2863 | indicate 2864 | indirect 2865 | industry 2866 | infected 2867 | informal 2868 | innocent 2869 | instance 2870 | intended 2871 | interest 2872 | interior 2873 | internal 2874 | internet 2875 | interval 2876 | involved 2877 | irritate 2878 | keyboard 2879 | kilogram 2880 | kindness 2881 | knitting 2882 | language 2883 | location 2884 | magazine 2885 | maintain 2886 | majority 2887 | marriage 2888 | matching 2889 | material 2890 | medicine 2891 | mentally 2892 | midnight 2893 | military 2894 | minister 2895 | ministry 2896 | minority 2897 | mistaken 2898 | moreover 2899 | mountain 2900 | movement 2901 | multiply 2902 | musician 2903 | national 2904 | negative 2905 | nineteen 2906 | nonsense 2907 | normally 2908 | northern 2909 | november 2910 | occasion 2911 | occupied 2912 | official 2913 | opponent 2914 | opposing 2915 | opposite 2916 | ordinary 2917 | organize 2918 | original 2919 | outdoors 2920 | overcome 2921 | painting 2922 | parallel 2923 | passport 2924 | patience 2925 | peaceful 2926 | personal 2927 | persuade 2928 | physical 2929 | planning 2930 | platform 2931 | pleasant 2932 | pleasing 2933 | pleasure 2934 | politely 2935 | politics 2936 | position 2937 | positive 2938 | possible 2939 | possibly 2940 | powerful 2941 | practice 2942 | practise 2943 | pregnant 2944 | premises 2945 | prepared 2946 | presence 2947 | preserve 2948 | pressure 2949 | previous 2950 | princess 2951 | printing 2952 | priority 2953 | prisoner 2954 | probable 2955 | probably 2956 | producer 2957 | progress 2958 | promptly 2959 | properly 2960 | property 2961 | proposal 2962 | prospect 2963 | provided 2964 | publicly 2965 | purchase 2966 | quantity 2967 | question 2968 | railroad 2969 | reaction 2970 | recently 2971 | regional 2972 | register 2973 | relation 2974 | relative 2975 | relaxing 2976 | relevant 2977 | religion 2978 | remember 2979 | repeated 2980 | research 2981 | resident 2982 | resource 2983 | response 2984 | restrict 2985 | revision 2986 | romantic 2987 | saturday 2988 | schedule 2989 | scissors 2990 | secretly 2991 | security 2992 | sensible 2993 | sentence 2994 | separate 2995 | severely 2996 | sexually 2997 | shocking 2998 | shooting 2999 | shopping 3000 | shoulder 3001 | sideways 3002 | sixtieth 3003 | slightly 3004 | smoothly 3005 | socially 3006 | software 3007 | solution 3008 | somebody 3009 | somewhat 3010 | southern 3011 | specific 3012 | spelling 3013 | standard 3014 | steadily 3015 | straight 3016 | stranger 3017 | strategy 3018 | strength 3019 | stressed 3020 | strictly 3021 | striking 3022 | strongly 3023 | struggle 3024 | suddenly 3025 | suitable 3026 | suitcase 3027 | superior 3028 | surprise 3029 | surround 3030 | swearing 3031 | swelling 3032 | swimming 3033 | sympathy 3034 | teaching 3035 | tendency 3036 | terrible 3037 | terribly 3038 | thinking 3039 | thirteen 3040 | thorough 3041 | thousand 3042 | threaten 3043 | thursday 3044 | together 3045 | tomorrow 3046 | training 3047 | transfer 3048 | triangle 3049 | tropical 3050 | trousers 3051 | ultimate 3052 | umbrella 3053 | unfairly 3054 | universe 3055 | unlikely 3056 | unsteady 3057 | upstairs 3058 | vacation 3059 | valuable 3060 | vertical 3061 | violence 3062 | weakness 3063 | whatever 3064 | whenever 3065 | wherever 3066 | withdraw 3067 | worrying 3068 | wrapping 3069 | yourself 3070 | abandoned 3071 | accompany 3072 | according 3073 | advantage 3074 | adventure 3075 | advertise 3076 | affection 3077 | afternoon 3078 | agreement 3079 | alcoholic 3080 | alongside 3081 | ambulance 3082 | anxiously 3083 | apartment 3084 | apologize 3085 | approving 3086 | assistant 3087 | associate 3088 | attempted 3089 | attention 3090 | authority 3091 | automatic 3092 | available 3093 | awkwardly 3094 | backwards 3095 | basically 3096 | beautiful 3097 | beginning 3098 | behaviour 3099 | boyfriend 3100 | breakfast 3101 | breathing 3102 | brilliant 3103 | broadcast 3104 | calculate 3105 | candidate 3106 | cardboard 3107 | carefully 3108 | celebrate 3109 | certainly 3110 | challenge 3111 | character 3112 | chemistry 3113 | chocolate 3114 | cigarette 3115 | classroom 3116 | colleague 3117 | committee 3118 | community 3119 | complaint 3120 | concerned 3121 | condition 3122 | confident 3123 | confusing 3124 | confusion 3125 | connected 3126 | conscious 3127 | construct 3128 | container 3129 | continent 3130 | correctly 3131 | criterion 3132 | criticism 3133 | criticize 3134 | curiously 3135 | currently 3136 | dangerous 3137 | delighted 3138 | departure 3139 | depressed 3140 | desperate 3141 | determine 3142 | different 3143 | difficult 3144 | direction 3145 | disappear 3146 | discovery 3147 | disgusted 3148 | dishonest 3149 | downwards 3150 | drugstore 3151 | education 3152 | effective 3153 | efficient 3154 | eightieth 3155 | elsewhere 3156 | embarrass 3157 | emergency 3158 | emotional 3159 | emphasize 3160 | encounter 3161 | encourage 3162 | enjoyable 3163 | enjoyment 3164 | entertain 3165 | equipment 3166 | essential 3167 | establish 3168 | everybody 3169 | excellent 3170 | exception 3171 | excluding 3172 | executive 3173 | existence 3174 | expensive 3175 | explosion 3176 | extension 3177 | extensive 3178 | extremely 3179 | favourite 3180 | fifteenth 3181 | financial 3182 | following 3183 | furniture 3184 | generally 3185 | gentleman 3186 | genuinely 3187 | geography 3188 | gradually 3189 | guarantee 3190 | happiness 3191 | highlight 3192 | household 3193 | hundredth 3194 | illegally 3195 | imaginary 3196 | immediate 3197 | impatient 3198 | important 3199 | impressed 3200 | inability 3201 | including 3202 | infection 3203 | influence 3204 | initially 3205 | institute 3206 | insulting 3207 | insurance 3208 | intention 3209 | interpret 3210 | interrupt 3211 | interview 3212 | introduce 3213 | invention 3214 | irritated 3215 | jewellery 3216 | judgement 3217 | justified 3218 | kilometre 3219 | knowledge 3220 | landscape 3221 | machinery 3222 | marketing 3223 | meanwhile 3224 | milligram 3225 | millionth 3226 | motorbike 3227 | naturally 3228 | necessary 3229 | neighbour 3230 | nervously 3231 | newspaper 3232 | ninetieth 3233 | objective 3234 | obviously 3235 | o�clock 3236 | offensive 3237 | operation 3238 | organized 3239 | otherwise 3240 | ourselves 3241 | packaging 3242 | passenger 3243 | perfectly 3244 | performer 3245 | permanent 3246 | photocopy 3247 | poisonous 3248 | political 3249 | pollution 3250 | potential 3251 | practical 3252 | precisely 3253 | president 3254 | primarily 3255 | principle 3256 | privately 3257 | procedure 3258 | professor 3259 | programme 3260 | promotion 3261 | pronounce 3262 | providing 3263 | publicity 3264 | qualified 3265 | realistic 3266 | reception 3267 | recognize 3268 | recommend 3269 | recording 3270 | reduction 3271 | reference 3272 | regarding 3273 | regularly 3274 | religious 3275 | remaining 3276 | represent 3277 | reproduce 3278 | satisfied 3279 | scientist 3280 | secondary 3281 | secretary 3282 | selection 3283 | sensitive 3284 | separated 3285 | september 3286 | seriously 3287 | seventeen 3288 | signature 3289 | similarly 3290 | sincerely 3291 | situation 3292 | skilfully 3293 | something 3294 | sometimes 3295 | somewhere 3296 | specially 3297 | spiritual 3298 | statement 3299 | strangely 3300 | structure 3301 | substance 3302 | suffering 3303 | supporter 3304 | surprised 3305 | suspicion 3306 | technical 3307 | technique 3308 | telephone 3309 | temporary 3310 | therefore 3311 | thickness 3312 | thirtieth 3313 | timetable 3314 | tradition 3315 | transform 3316 | translate 3317 | transport 3318 | traveller 3319 | treatment 3320 | twentieth 3321 | typically 3322 | uncertain 3323 | underwear 3324 | unusually 3325 | unwilling 3326 | upsetting 3327 | variation 3328 | vegetable 3329 | violently 3330 | virtually 3331 | wednesday 3332 | willingly 3333 | wonderful 3334 | yesterday 3335 | absolutely 3336 | acceptable 3337 | accidental 3338 | accurately 3339 | additional 3340 | adequately 3341 | admiration 3342 | afterwards 3343 | aggressive 3344 | altogether 3345 | anticipate 3346 | apparently 3347 | appearance 3348 | appreciate 3349 | artificial 3350 | assistance 3351 | associated 3352 | atmosphere 3353 | attraction 3354 | attractive 3355 | background 3356 | carelessly 3357 | centimetre 3358 | chairwoman 3359 | cheerfully 3360 | collection 3361 | commercial 3362 | commission 3363 | commitment 3364 | comparison 3365 | completely 3366 | complicate 3367 | concerning 3368 | conclusion 3369 | conference 3370 | confidence 3371 | connection 3372 | constantly 3373 | continuous 3374 | contribute 3375 | controlled 3376 | convenient 3377 | convention 3378 | decoration 3379 | decorative 3380 | definitely 3381 | definition 3382 | deliberate 3383 | department 3384 | depressing 3385 | determined 3386 | dictionary 3387 | difference 3388 | difficulty 3389 | disappoint 3390 | disapprove 3391 | discipline 3392 | discussion 3393 | disgusting 3394 | distribute 3395 | disturbing 3396 | downstairs 3397 | eighteenth 3398 | electrical 3399 | electronic 3400 | employment 3401 | enthusiasm 3402 | equivalent 3403 | especially 3404 | eventually 3405 | everything 3406 | everywhere 3407 | exaggerate 3408 | excitement 3409 | exhibition 3410 | experience 3411 | experiment 3412 | expression 3413 | faithfully 3414 | foundation 3415 | fourteenth 3416 | frequently 3417 | friendship 3418 | frightened 3419 | generation 3420 | generously 3421 | girlfriend 3422 | government 3423 | grandchild 3424 | historical 3425 | horizontal 3426 | illustrate 3427 | importance 3428 | impossible 3429 | impression 3430 | impressive 3431 | indication 3432 | indirectly 3433 | individual 3434 | industrial 3435 | inevitable 3436 | inevitably 3437 | infectious 3438 | ingredient 3439 | initiative 3440 | instrument 3441 | interested 3442 | investment 3443 | invitation 3444 | irritating 3445 | journalist 3446 | laboratory 3447 | literature 3448 | management 3449 | membership 3450 | millimetre 3451 | motorcycle 3452 | mysterious 3453 | nineteenth 3454 | noticeable 3455 | officially 3456 | opposition 3457 | originally 3458 | parliament 3459 | particular 3460 | permission 3461 | personally 3462 | philosophy 3463 | photograph 3464 | physically 3465 | pleasantly 3466 | politician 3467 | population 3468 | possession 3469 | preference 3470 | presumably 3471 | previously 3472 | production 3473 | profession 3474 | proportion 3475 | protection 3476 | publishing 3477 | punishment 3478 | reasonable 3479 | reasonably 3480 | regulation 3481 | relatively 3482 | remarkable 3483 | remarkably 3484 | repeatedly 3485 | reputation 3486 | resistance 3487 | restaurant 3488 | restricted 3489 | retirement 3490 | revolution 3491 | ridiculous 3492 | satisfying 3493 | scientific 3494 | separately 3495 | separation 3496 | seventieth 3497 | specialist 3498 | substitute 3499 | successful 3500 | sufficient 3501 | suggestion 3502 | surprising 3503 | suspicious 3504 | technology 3505 | television 3506 | themselves 3507 | thirteenth 3508 | thoroughly 3509 | thousandth 3510 | throughout 3511 | ultimately 3512 | underneath 3513 | understand 3514 | underwater 3515 | unemployed 3516 | unexpected 3517 | unfriendly 3518 | university 3519 | unpleasant 3520 | achievement 3521 | acknowledge 3522 | advertising 3523 | alternative 3524 | anniversary 3525 | application 3526 | appointment 3527 | appropriate 3528 | approximate 3529 | arrangement 3530 | association 3531 | beautifully 3532 | businessman 3533 | calculation 3534 | celebration 3535 | certificate 3536 | combination 3537 | comfortable 3538 | comfortably 3539 | communicate 3540 | competition 3541 | competitive 3542 | complicated 3543 | concentrate 3544 | confidently 3545 | consequence 3546 | contrasting 3547 | countryside 3548 | demonstrate 3549 | description 3550 | desperately 3551 | destruction 3552 | development 3553 | differently 3554 | disapproval 3555 | dishonestly 3556 | distinguish 3557 | effectively 3558 | efficiently 3559 | electricity 3560 | embarrassed 3561 | emotionally 3562 | engineering 3563 | entertainer 3564 | environment 3565 | essentially 3566 | exaggerated 3567 | examination 3568 | expectation 3569 | experienced 3570 | explanation 3571 | fashionable 3572 | frightening 3573 | fundamental 3574 | grandfather 3575 | grandmother 3576 | grandparent 3577 | hairdresser 3578 | imagination 3579 | immediately 3580 | implication 3581 | importantly 3582 | improvement 3583 | independent 3584 | information 3585 | institution 3586 | instruction 3587 | intelligent 3588 | interesting 3589 | investigate 3590 | involvement 3591 | manufacture 3592 | mathematics 3593 | measurement 3594 | necessarily 3595 | observation 3596 | opportunity 3597 | outstanding 3598 | partnership 3599 | performance 3600 | permanently 3601 | personality 3602 | photography 3603 | politically 3604 | possibility 3605 | potentially 3606 | practically 3607 | preparation 3608 | publication 3609 | recognition 3610 | requirement 3611 | reservation 3612 | responsible 3613 | restriction 3614 | significant 3615 | substantial 3616 | supermarket 3617 | surrounding 3618 | sympathetic 3619 | temperature 3620 | temporarily 3621 | threatening 3622 | traditional 3623 | translation 3624 | transparent 3625 | unconscious 3626 | underground 3627 | unfortunate 3628 | unimportant 3629 | unnecessary 3630 | unwillingly 3631 | accidentally 3632 | alphabetical 3633 | artificially 3634 | artistically 3635 | circumstance 3636 | congratulate 3637 | conservative 3638 | considerable 3639 | considerably 3640 | construction 3641 | contemporary 3642 | continuously 3643 | contribution 3644 | conventional 3645 | conversation 3646 | deliberately 3647 | disadvantage 3648 | disagreement 3649 | disappointed 3650 | disapproving 3651 | distribution 3652 | dramatically 3653 | embarrassing 3654 | entertaining 3655 | enthusiastic 3656 | increasingly 3657 | independence 3658 | intelligence 3659 | interruption 3660 | introduction 3661 | manufacturer 3662 | nevertheless 3663 | occasionally 3664 | organization 3665 | particularly 3666 | photographer 3667 | presentation 3668 | professional 3669 | refrigerator 3670 | relationship 3671 | satisfaction 3672 | specifically 3673 | successfully 3674 | sufficiently 3675 | surprisingly 3676 | surroundings 3677 | unacceptable 3678 | uncontrolled 3679 | unemployment 3680 | unexpectedly 3681 | unreasonable 3682 | unsuccessful 3683 | accommodation 3684 | advertisement 3685 | alternatively 3686 | approximately 3687 | automatically 3688 | communication 3689 | concentration 3690 | consideration 3691 | determination 3692 | disappointing 3693 | embarrassment 3694 | encouragement 3695 | entertainment 3696 | environmental 3697 | extraordinary 3698 | granddaughter 3699 | independently 3700 | international 3701 | investigation 3702 | manufacturing 3703 | neighbourhood 3704 | pronunciation 3705 | qualification 3706 | significantly 3707 | substantially 3708 | traditionally 3709 | uncomfortable 3710 | understanding 3711 | unfortunately 3712 | alphabetically 3713 | characteristic 3714 | congratulation 3715 | disappointment 3716 | interpretation 3717 | representative 3718 | responsibility 3719 | transportation 3720 | enthusiastically""".split( 3721 | "\n" 3722 | ) 3723 | 3724 | 3725 | if __name__ == "__main__": 3726 | game = Hangman() 3727 | game.start() 3728 | -------------------------------------------------------------------------------- /quickpython/examples/memory.py: -------------------------------------------------------------------------------- 1 | """ 2 | /games/memory.py 3 | 4 | Copyright (c) 2019 ShineyDev 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | 19 | __authors__ = [("shineydev", "contact@shiney.dev")] 20 | __maintainers__ = [("shineydev", "contact@shiney.dev")] 21 | 22 | __version_info__ = (1, 0, 0, "final", 0) 23 | __version__ = "{0}.{1}.{2}{3}{4}".format( 24 | *[str(n)[0] if (i == 3) else str(n) for (i, n) in enumerate(__version_info__)] 25 | ) 26 | 27 | 28 | import os 29 | import random 30 | import re 31 | import string 32 | import time 33 | 34 | import pyfiglet 35 | 36 | GRID_SIZES = {"easy": 6, "intermediate": 10, "hard": 14} 37 | 38 | CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"£$%^&*()-_=+[{]};:'@#~,<.>/?\\|`¬áé" 39 | 40 | 41 | class Memory: 42 | def __init__(self, difficulty: str): 43 | """ 44 | initializes a `Memory` object 45 | """ 46 | 47 | self.grid_size = GRID_SIZES[difficulty] 48 | self.characters = CHARACTERS[: (((self.grid_size // 2) ** 2) * 2)] 49 | 50 | self.message = "" 51 | 52 | def game(self): 53 | """ 54 | starts the game 55 | """ 56 | 57 | self.known = [" "] * len(self.characters * 2) 58 | characters = [character for character in self.characters * 2] 59 | 60 | random.shuffle(characters) 61 | 62 | while " " in self.known: 63 | cls() 64 | print() 65 | 66 | self.show_grid() 67 | 68 | print() 69 | 70 | if self.message: 71 | print(self.message) 72 | print() 73 | 74 | self.message = "" 75 | 76 | coordinates = input("coordinates;\n> ").strip().upper() 77 | if not self.valid_coordinates(coordinates): 78 | self.message = "invalid coordinates" 79 | continue 80 | 81 | first_row, first_column = self.parse_coordinates(coordinates) 82 | 83 | if self.known[(first_row * self.grid_size) + first_column] != " ": 84 | self.message = "invalid coordinates" 85 | continue 86 | 87 | self.known[(first_row * self.grid_size) + first_column] = characters[ 88 | (first_row * self.grid_size) + first_column 89 | ] 90 | 91 | while True: 92 | cls() 93 | print() 94 | 95 | self.show_grid() 96 | 97 | print() 98 | 99 | if self.message: 100 | print(self.message) 101 | print() 102 | 103 | self.message = "" 104 | 105 | coordinates = input("> ").strip().upper() 106 | if self.valid_coordinates(coordinates): 107 | second_row, second_column = self.parse_coordinates(coordinates) 108 | 109 | if self.known[(second_row * self.grid_size) + second_column] != " ": 110 | self.message = "invalid coordinates" 111 | continue 112 | 113 | self.known[(second_row * self.grid_size) + second_column] = characters[ 114 | (second_row * self.grid_size) + second_column 115 | ] 116 | break 117 | 118 | self.message = "invalid coordinates" 119 | 120 | cls() 121 | print() 122 | 123 | self.show_grid() 124 | 125 | time.sleep(1) 126 | 127 | if ( 128 | not self.known[(first_row * self.grid_size) + first_column] 129 | == self.known[(second_row * self.grid_size) + second_column] 130 | ): 131 | self.known[(first_row * self.grid_size) + first_column] = " " 132 | self.known[(second_row * self.grid_size) + second_column] = " " 133 | 134 | cls() 135 | print() 136 | 137 | self.show_grid() 138 | 139 | print() 140 | print("congrats") 141 | 142 | def parse_coordinates(self, coordinates: str): 143 | """ 144 | generates a (row : int, column : int) tuple from coordinates 145 | 146 | arguments: 147 | coordinates :: str :: the coordinates convert 148 | 149 | returns: 150 | :: tuple :: a (row : int, column : int) tuple 151 | """ 152 | 153 | pattern = re.compile( 154 | r"(?P[A-{0}])(?P{1})".format( 155 | string.ascii_uppercase[self.grid_size - 1], 156 | "|".join([str(i) for i in range(1, self.grid_size + 1)][::-1]), 157 | ) 158 | ) 159 | match = re.match(pattern, coordinates) 160 | 161 | row = int(match.group("row")) - 1 162 | column = string.ascii_uppercase.index(match.group("column")) 163 | 164 | return (row, column) 165 | 166 | def show_grid(self): 167 | """""" 168 | 169 | print( 170 | " {0}".format( 171 | " ".join([character for character in string.ascii_lowercase][: self.grid_size]) 172 | ) 173 | ) 174 | print(" {0}".format("-" * ((self.grid_size * 4) + 1))) 175 | 176 | i = 1 177 | for (j, k) in enumerate(self.known, 1): 178 | if j % (self.grid_size) == 0: 179 | print("| {0} |".format(k)) 180 | print(" {0}".format("-" * ((self.grid_size * 4) + 1))) 181 | elif j % (self.grid_size) == 1: 182 | print(" {0:>2} | {1} ".format(i, k), end="") 183 | i += 1 184 | else: 185 | print("| {0} ".format(k), end="") 186 | 187 | def start(self): 188 | """ 189 | calls `self.game` in a 'would you like to play again?' loop 190 | """ 191 | 192 | choice = "y" 193 | while choice.startswith("y"): 194 | cls() 195 | print(pyfiglet.figlet_format("Memory")) 196 | print() 197 | input("enter to play\nctrl + c to quit to main menu\n\n") 198 | 199 | self.game() 200 | choice = input("\nwould you like to play again?\n> ").strip() 201 | 202 | def valid_coordinates(self, coordinates: str): 203 | """ 204 | determines whether `coordinates` are valid 205 | 206 | arguments: 207 | coordinates :: str :: the coordinates to check 208 | 209 | returns: 210 | :: bool :: whether `coordinates` are valid 211 | """ 212 | 213 | pattern = re.compile( 214 | r"(?P[A-{0}])(?P{1})".format( 215 | string.ascii_uppercase[self.grid_size - 1], 216 | "|".join([str(i) for i in range(1, self.grid_size + 1)][::-1]), 217 | ) 218 | ) 219 | match = re.match(pattern, coordinates) 220 | 221 | if match: 222 | return True 223 | return False 224 | 225 | 226 | if __name__ == "__main__": 227 | difficulty = None 228 | while difficulty not in {"easy", "intermediate", "hard"}: 229 | cls() 230 | print() 231 | difficulty = input("difficulty;\n> ").strip() 232 | 233 | game = Memory(difficulty) 234 | game.start() 235 | -------------------------------------------------------------------------------- /quickpython/examples/minesweeper.py: -------------------------------------------------------------------------------- 1 | """ 2 | /games/minesweeper.py 3 | 4 | Copyright (c) 2019 ShineyDev 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | 19 | __authors__ = [("shineydev", "contact@shiney.dev")] 20 | __maintainers__ = [("shineydev", "contact@shiney.dev")] 21 | 22 | __version_info__ = (1, 0, 0, "final", 0) 23 | __version__ = "{0}.{1}.{2}{3}{4}".format( 24 | *[str(n)[0] if (i == 3) else str(n) for (i, n) in enumerate(__version_info__)] 25 | ) 26 | 27 | 28 | import os 29 | import random 30 | import re 31 | import string 32 | import time 33 | 34 | import colorama 35 | import pyfiglet 36 | 37 | GRID_SIZES = {"easy": 10, "intermediate": 18, "hard": 20} 38 | 39 | MINE_COUNTS = {"easy": 10, "intermediate": 50, "hard": 100} 40 | 41 | COLORS = [ 42 | colorama.Fore.LIGHTYELLOW_EX, 43 | colorama.Fore.YELLOW, 44 | colorama.Fore.LIGHTMAGENTA_EX, 45 | colorama.Fore.MAGENTA, 46 | colorama.Fore.LIGHTRED_EX, 47 | colorama.Fore.RED, 48 | colorama.Fore.LIGHTCYAN_EX, 49 | colorama.Fore.CYAN, 50 | ] 51 | 52 | 53 | class Grid: 54 | def __init__(self, size: int, mine_count: int): 55 | """ 56 | initializes a `Grid` object 57 | """ 58 | 59 | self.size = size 60 | self.mine_count = mine_count 61 | 62 | self.known = [[" " for (i) in range(self.size)] for (j) in range(self.size)] 63 | 64 | def get_neighbors(self, cell: tuple) -> set: 65 | """ 66 | gets a set of surrounding cells from `cell` 67 | 68 | arguments: 69 | cell :: tuple :: the cell to surround 70 | 71 | returns: 72 | neighbors :: set :: a set of surrounding cells 73 | """ 74 | 75 | row_index, column_index = cell 76 | 77 | neighbors = set() 78 | 79 | for i in range(-1, 2): 80 | for j in range(-1, 2): 81 | if (i == 0) and (j == 0): 82 | # this is `cell` 83 | continue 84 | elif (-1 < (row_index + i) < self.size) and (-1 < (column_index + j) < self.size): 85 | neighbors.add((row_index + i, column_index + j)) 86 | 87 | return neighbors 88 | 89 | def get_random_cell(self) -> tuple: 90 | """ 91 | gets a random (row, column) tuple 92 | 93 | returns: 94 | :: tuple :: a random (row, column) tuple 95 | """ 96 | 97 | return (random.randint(0, self.size - 1), random.randint(0, self.size - 1)) 98 | 99 | def generate_mines(self, start_cell: tuple): 100 | """ 101 | generates a set of mines excluding `start_cell` 102 | 103 | arguments: 104 | start_cell :: tuple :: the player's first selected cell 105 | """ 106 | 107 | self.mines = set() 108 | neighbors = self.get_neighbors(start_cell) 109 | 110 | while len(self.mines) != self.mine_count: 111 | cell = self.get_random_cell() 112 | 113 | if (cell == start_cell) or (cell in neighbors): 114 | continue 115 | 116 | self.mines.add(cell) 117 | 118 | for (row, column) in self.mines: 119 | self.hidden[row][column] = "X" 120 | 121 | def generate_numbers(self): 122 | """ 123 | generates a number for each cell based on surrounding mines 124 | """ 125 | 126 | for (row_index, row) in enumerate(self.hidden): 127 | for (column_index, cell) in enumerate(row): 128 | if cell != "X": 129 | values = [ 130 | self.hidden[row][column] 131 | for (row, column) in self.get_neighbors((row_index, column_index)) 132 | ] 133 | number = values.count("X") 134 | 135 | if number == 0: 136 | self.hidden[row_index][column_index] = "0" 137 | else: 138 | self.hidden[row_index][column_index] = "{0}{1}{2}".format( 139 | COLORS[number - 1], number, colorama.Fore.RESET 140 | ) 141 | 142 | def show(self, grid: list) -> str: 143 | """ 144 | generates a string containing a readable form of the minesweeper grid 145 | 146 | arguments: 147 | grid :: list :: the minesweeper grid to generate 148 | 149 | returns: 150 | grid_string :: str :: the readable form of `grid` 151 | """ 152 | 153 | horizontal = " " + ("-" * (4 * len(grid))) + "-" 154 | top_label = " " 155 | 156 | for character in string.ascii_uppercase[: len(grid)]: 157 | top_label += "{0} ".format(character) 158 | 159 | grid_string = "{0}\n{1}".format(top_label, horizontal) 160 | 161 | for (i, j) in enumerate(grid, 1): 162 | row = "\n{0:>2} |".format(i) 163 | 164 | for k in j: 165 | row += " {0} |".format(k) 166 | 167 | grid_string += "{0}\n{1}".format(row, horizontal) 168 | 169 | return grid_string 170 | 171 | def show_cell(self, cell: tuple): 172 | """ 173 | sets `cell` in `self.known` as `self.hidden` and iterates through neighbors if `self.known` == "0" 174 | 175 | arguments: 176 | cell :: tuple :: the cell to show 177 | """ 178 | 179 | row_index, column_index = cell 180 | 181 | if self.known[row_index][column_index] != " ": 182 | return 183 | 184 | self.known[row_index][column_index] = self.hidden[row_index][column_index] 185 | 186 | if self.known[row_index][column_index] == "0": 187 | for (row_index, column_index) in self.get_neighbors((row_index, column_index)): 188 | if self.known[row_index][column_index] != "F": 189 | self.show_cell((row_index, column_index)) 190 | 191 | def start(self, start_cell: tuple): 192 | """ 193 | generates the minesweeper grid 194 | 195 | arguments: 196 | start_cell :: tuple :: passed into `self.generate_mines` and `self.show_cell` 197 | """ 198 | 199 | self.hidden = [["0" for (i) in range(self.size)] for (j) in range(self.size)] 200 | 201 | self.generate_mines(start_cell) 202 | self.generate_numbers() 203 | 204 | self.show_cell(start_cell) 205 | 206 | 207 | class Minesweeper: 208 | def __init__(self, difficulty: str): 209 | """ 210 | initializes a `Minesweeper` object 211 | """ 212 | 213 | self.grid_size = GRID_SIZES[difficulty] 214 | self.mine_count = MINE_COUNTS[difficulty] 215 | 216 | def game(self): 217 | """ 218 | starts the game 219 | """ 220 | 221 | self.grid = Grid(self.grid_size, self.mine_count) 222 | self.message = "" 223 | 224 | coordinates = "" 225 | while not self.valid_coordinates(coordinates) or (coordinates.endswith("F")): 226 | cls() 227 | 228 | print() 229 | print(self.grid.show(self.grid.known)) 230 | print() 231 | 232 | coordinates = input("coordinates;\n> ").strip().upper() 233 | if not self.valid_coordinates(coordinates): 234 | self.message = "invalid coordinates" 235 | continue 236 | 237 | row, column, flag = self.parse_coordinates(coordinates) 238 | 239 | self.grid.start((row, column)) 240 | flags = set() 241 | 242 | while not flags == self.grid.mines: 243 | cls() 244 | 245 | print() 246 | print(self.grid.show(self.grid.known)) 247 | print() 248 | 249 | if self.message: 250 | print(self.message) 251 | print() 252 | 253 | self.message = "" 254 | else: 255 | print( 256 | "{0} mine{1} left".format( 257 | len(self.grid.mines) - len(flags), 258 | "s" if (len(self.grid.mines) - len(flags) != 1) else "", 259 | ) 260 | ) 261 | print() 262 | 263 | coordinates = input("coordinates;\n> ").strip().upper() 264 | 265 | if coordinates.startswith("EVAL("): 266 | # this entire block of code was me messing with the game and making cheats bc i got bored :)) 267 | 268 | # commands are; 269 | # eval(va-`column``row`) -> sets self.message to the value at `column`, `row` 270 | # eval(win) -> does some freaky looking printing to generate mines and win the game 271 | 272 | pattern_string = r"^EVAL\(VA-(?P[A-{0}])(?P{1})\)$".format( 273 | string.ascii_uppercase[self.grid_size - 1], 274 | "|".join([str(i) for i in range(1, self.grid_size + 1)][::-1]), 275 | ) 276 | pattern = re.compile(pattern_string) 277 | match = re.match(pattern, coordinates) 278 | 279 | if match: 280 | row = int(match.group("row")) - 1 281 | column = string.ascii_uppercase.index(match.group("column")) 282 | 283 | self.message = self.grid.hidden[row][column] 284 | continue 285 | 286 | pattern_string = r"^EVAL\(WIN\)$" 287 | pattern = re.compile(pattern_string) 288 | match = re.match(pattern, coordinates) 289 | 290 | if match: 291 | for i in range(self.grid_size): 292 | for j in range(self.grid_size): 293 | if self.grid.known[i][j] != " ": 294 | continue 295 | 296 | if self.grid.hidden[i][j] != "X": 297 | continue 298 | 299 | flags.add((i, j)) 300 | self.grid.known[i][j] = "F" 301 | 302 | cls() 303 | 304 | print() 305 | print(self.grid.show(self.grid.known)) 306 | print() 307 | 308 | print( 309 | "{0} mine{1} left".format( 310 | len(self.grid.mines) - len(flags), 311 | "s" if (len(self.grid.mines) - len(flags) != 1) else "", 312 | ) 313 | ) 314 | 315 | continue 316 | 317 | if not self.valid_coordinates(coordinates): 318 | self.message = "invalid coordinates" 319 | continue 320 | 321 | row, column, flag = self.parse_coordinates(coordinates) 322 | 323 | if flag: 324 | if self.grid.known[row][column] == " ": 325 | self.grid.known[row][column] = "F" 326 | flags.add((row, column)) 327 | elif self.grid.known[row][column] == "F": 328 | self.grid.known[row][column] = " " 329 | flags.remove((row, column)) 330 | else: 331 | self.message = "cannot put a flag there" 332 | elif (row, column) in flags: 333 | self.message = "there is a flag there" 334 | elif self.grid.hidden[row][column] == "X": 335 | cls() 336 | 337 | print() 338 | print(self.grid.show(self.grid.hidden)) 339 | print() 340 | print("you lose") 341 | 342 | return 343 | elif self.grid.known[row][column] == " ": 344 | self.grid.show_cell((row, column)) 345 | else: 346 | self.message = "that cell is already shown" 347 | 348 | cls() 349 | 350 | print() 351 | print(self.grid.show(self.grid.hidden)) 352 | print() 353 | print("you win") 354 | 355 | def parse_coordinates(self, coordinates: str) -> tuple: 356 | """ 357 | generates a (row : int, column : int, flag : bool) tuple from coordinates 358 | 359 | arguments: 360 | coordinates :: str :: the coordinates convert 361 | 362 | returns: 363 | :: tuple :: a (row : int, column : int, flag : bool) tuple 364 | """ 365 | 366 | pattern = re.compile( 367 | r"^(?P[A-{0}])(?P{1})(?PF?)$".format( 368 | string.ascii_uppercase[self.grid_size - 1], 369 | "|".join([str(i) for i in range(1, self.grid_size + 1)][::-1]), 370 | ) 371 | ) 372 | match = re.match(pattern, coordinates) 373 | 374 | row = int(match.group("row")) - 1 375 | column = string.ascii_uppercase.index(match.group("column")) 376 | flag = match.group("flag") == "F" 377 | 378 | return (row, column, flag) 379 | 380 | def start(self): 381 | """ 382 | calls `self.game` in a 'would you like to play again?' loop 383 | """ 384 | 385 | choice = "y" 386 | while choice.startswith("y"): 387 | cls() 388 | print(pyfiglet.figlet_format("Minesweeper")) 389 | print() 390 | input("enter to play\nctrl + c to quit to main menu\n\n") 391 | 392 | self.game() 393 | choice = input("\nwould you like to play again?\n> ").strip() 394 | 395 | def valid_coordinates(self, coordinates: str) -> bool: 396 | """ 397 | determines whether `coordinates` are valid 398 | 399 | arguments: 400 | coordinates :: str :: the coordinates to check 401 | 402 | returns: 403 | :: bool :: whether `coordinates` are valid 404 | """ 405 | 406 | pattern = re.compile( 407 | r"^(?P[A-{0}])(?P{1})(?PF?)$".format( 408 | string.ascii_uppercase[self.grid_size - 1], 409 | "|".join([str(i) for i in range(1, self.grid_size + 1)][::-1]), 410 | ) 411 | ) 412 | match = re.match(pattern, coordinates) 413 | 414 | if match: 415 | return True 416 | return False 417 | 418 | 419 | if __name__ == "__main__": 420 | difficulty = None 421 | while difficulty not in {"easy", "intermediate", "hard"}: 422 | cls() 423 | print() 424 | difficulty = input("difficulty;\n> ").strip() 425 | 426 | game = Minesweeper(difficulty) 427 | game.start() 428 | -------------------------------------------------------------------------------- /quickpython/examples/simon.py: -------------------------------------------------------------------------------- 1 | """ 2 | /games/simon.py 3 | 4 | Copyright (c) 2019 ShineyDev 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | 19 | __authors__ = [("shineydev", "contact@shiney.dev")] 20 | __maintainers__ = [("shineydev", "contact@shiney.dev")] 21 | 22 | __version_info__ = (1, 0, 0, "final", 0) 23 | __version__ = "{0}.{1}.{2}{3}{4}".format( 24 | *[str(n)[0] if (i == 3) else str(n) for (i, n) in enumerate(__version_info__)] 25 | ) 26 | 27 | 28 | import os 29 | import random 30 | import time 31 | 32 | import colorama 33 | import pyfiglet 34 | 35 | COLORS = { 36 | "easy": [ 37 | (colorama.Back.RED, "red"), 38 | (colorama.Back.GREEN, "green"), 39 | (colorama.Back.BLUE, "blue"), 40 | (colorama.Back.YELLOW, "yellow"), 41 | ], 42 | "intermediate": [ 43 | (colorama.Back.RED, "red"), 44 | (colorama.Back.GREEN, "green"), 45 | (colorama.Back.BLUE, "blue"), 46 | (colorama.Back.YELLOW, "yellow"), 47 | (colorama.Back.CYAN, "cyan"), 48 | ], 49 | "hard": [ 50 | (colorama.Back.GREEN, "green"), 51 | (colorama.Back.BLUE, "blue"), 52 | (colorama.Back.YELLOW, "yellow"), 53 | (colorama.Back.CYAN, "cyan"), 54 | (colorama.Back.MAGENTA, "magenta"), 55 | ], 56 | } 57 | 58 | TIMES = {"easy": 0.5, "intermediate": 0.4, "hard": 0.3} 59 | 60 | 61 | class Simon: 62 | def __init__(self, difficulty: str): 63 | """ 64 | initializes a `Simon` object 65 | """ 66 | 67 | self.colors = COLORS[difficulty] 68 | self.time = TIMES[difficulty] 69 | 70 | def game(self): 71 | """ 72 | starts the game 73 | """ 74 | 75 | colors = list() 76 | 77 | while True: 78 | colors.append(random.choice(self.colors)) 79 | 80 | for (color, color_name) in colors: 81 | cls() 82 | print( 83 | """ 84 | ------------------ 85 | |{0} {1}| 86 | |{0} {1}| 87 | |{0} {1}| 88 | |{0} {1}| 89 | |{0} {1}| 90 | |{0} {1}| 91 | |{0} {1}| 92 | ------------------ 93 | """.format( 94 | color, colorama.Back.RESET 95 | ) 96 | ) 97 | 98 | time.sleep(self.time) 99 | 100 | cls() 101 | print( 102 | """ 103 | ------------------ 104 | | | 105 | | | 106 | | | 107 | | | 108 | | | 109 | | | 110 | | | 111 | ------------------ 112 | """ 113 | ) 114 | 115 | time.sleep(self.time) 116 | 117 | print() 118 | 119 | answer = input("> ").replace(" ", "") 120 | correct = "".join([color_name[0] for (color, color_name) in colors]) 121 | 122 | if answer != correct: 123 | break 124 | 125 | print() 126 | print("you got {0} correct combinations".format(len(colors) - 1)) 127 | 128 | def start(self): 129 | """ 130 | calls `self.game` in a 'would you like to play again?' loop 131 | """ 132 | 133 | choice = "y" 134 | while choice.startswith("y"): 135 | cls() 136 | print(pyfiglet.figlet_format("Simon Says")) 137 | print() 138 | input("enter to play\nctrl + c to quit to main menu\n\n") 139 | 140 | self.game() 141 | choice = input("\nwould you like to play again?\n> ").strip() 142 | 143 | 144 | if __name__ == "__main__": 145 | difficulty = None 146 | while difficulty not in {"easy", "intermediate", "hard"}: 147 | cls() 148 | print() 149 | difficulty = input("difficulty;\n> ").strip() 150 | 151 | game = Simon(difficulty) 152 | game.start() 153 | -------------------------------------------------------------------------------- /quickpython/examples/tictactoe.py: -------------------------------------------------------------------------------- 1 | """Ultimate Tic-Tac-Toe, by Al Sweigart al@inventwithpython.com 2 | Instead of a board with 9 spaces, this game has 9 boards with 81 spaces, 3 | the winner of each board placing their X or O on the big board! 4 | More info at: https://en.wikipedia.org/wiki/Ultimate_tic-tac-toe 5 | This and other games are available at https://nostarch.com/XX 6 | Tags: large, game, board game, two-player""" 7 | __version__ = 0 8 | import sys 9 | 10 | # Set up the constants: 11 | O_PLAYER = "O" 12 | X_PLAYER = "X" 13 | TIED = "tied" 14 | EMPTY_SPACE = "." 15 | BOARD_WIDTH = 3 16 | BOARD_HEIGHT = 3 17 | CANVAS_WIDTH = 15 18 | CANVAS_HEIGHT = 9 19 | SUBCANVAS_WIDTH = 5 20 | SUBCANVAS_HEIGHT = 3 21 | 22 | 23 | def main(): 24 | print( 25 | """Ultimate Tic-Tac-Toe, by Al Sweigart al@inventwithpython.com 26 | Instead of tic-tac-toe with 9 spaces, this game has a "big" board 27 | made up of 9 "small" tic-tac-toe boards. Moving on a small board causes 28 | the next player to move on that relative board. Winning on a small board 29 | lets that player put their mark on the big board. The winner must get 30 | three in a row on the big board. 31 | """ 32 | ) 33 | 34 | turn = X_PLAYER # X will go first. 35 | gameBoard = getNewBoard() 36 | 37 | # focusX and focusY determine which small board the player moves on. 38 | # If they are both None, the player can choose a small board. 39 | focusX, focusY = None, None 40 | while True: # Main game loop. 41 | displayBoard(gameBoard) 42 | focusX, focusY = askForPlayerMove(turn, gameBoard, focusX, focusY) 43 | 44 | # Check for a big board winner: 45 | bigBoard = makeBoardFromSmallBoards(gameBoard) 46 | bigWinner = getWinner(bigBoard) 47 | if bigWinner == TIED: 48 | displayBoard(gameBoard) 49 | print("The game is a tie!") 50 | print("Thanks for playing!") 51 | sys.exit() 52 | elif bigWinner != None: 53 | displayBoard(gameBoard) 54 | print(bigWinner, "has won!") 55 | print("Thanks for playing!") 56 | sys.exit() 57 | 58 | # Switch to the other player's turn: 59 | if turn == X_PLAYER: 60 | turn = O_PLAYER 61 | elif turn == O_PLAYER: 62 | turn = X_PLAYER 63 | 64 | 65 | def getNewBoard(): 66 | """Returns a dictionary that represents the big tic-tac-toe board. 67 | Keys are (x, y) int tuples that span from 0 to 2, the values are 68 | dictonaries that represent small tic-tac-toe boards. These 69 | dictionaries have (x, y) int tuples as well, and their values are 70 | either X_PLAYER, O_PLAYER, or EMPTY_SPACE.""" 71 | board = {} 72 | # Loop over each small board: 73 | for x in range(BOARD_WIDTH): 74 | for y in range(BOARD_HEIGHT): 75 | board[(x, y)] = {} 76 | # Loop over each space on the small board: 77 | for smallX in range(BOARD_WIDTH): 78 | for smallY in range(BOARD_HEIGHT): 79 | board[(x, y)][(smallX, smallY)] = EMPTY_SPACE 80 | return board 81 | 82 | 83 | def displayBoard(board): 84 | """Displays the big tic-tac-toe board on the screen.""" 85 | # The canvas is a dictionary that has keys of (x, y) tuples, and 86 | # the values are the character to print at that place on the screen. 87 | canvas = {} 88 | # First, put blank spaces on the entire canvas: 89 | for x in range(CANVAS_WIDTH): 90 | for y in range(CANVAS_HEIGHT): 91 | canvas[(x, y)] = " " 92 | 93 | # Second, fill in the big board Xs and Os on the canvas: 94 | for x in range(BOARD_WIDTH): 95 | for y in range(BOARD_HEIGHT): 96 | winner = getWinner(board[(x, y)]) 97 | if winner == X_PLAYER: 98 | # Draw a large X for each small board X won: 99 | canvas[(x * 5 + 1, y * 3 + 0)] = "\\" 100 | canvas[(x * 5 + 3, y * 3 + 0)] = "/" 101 | canvas[(x * 5 + 2, y * 3 + 1)] = "X" 102 | canvas[(x * 5 + 1, y * 3 + 2)] = "/" 103 | canvas[(x * 5 + 3, y * 3 + 2)] = "\\" 104 | elif winner == O_PLAYER: 105 | # Draw a large O for each small board O won: 106 | canvas[(x * 5 + 2, y * 3 + 0)] = "_" 107 | canvas[(x * 5 + 1, y * 3 + 1)] = "/" 108 | canvas[(x * 5 + 3, y * 3 + 1)] = "\\" 109 | canvas[(x * 5 + 1, y * 3 + 2)] = "\\" 110 | canvas[(x * 5 + 2, y * 3 + 2)] = "_" 111 | canvas[(x * 5 + 3, y * 3 + 2)] = "/" 112 | elif winner == TIED: 113 | # Draw a large ### block for tied small boards: 114 | for scx in range(SUBCANVAS_WIDTH): 115 | for scy in range(SUBCANVAS_HEIGHT): 116 | canvas[(x * 5 + scx, y * 3 + scy)] = "#" 117 | 118 | # Third, fill in the Xs and Os of the small boards on the canvas: 119 | for ix, smallTopLeftX in enumerate([0, 5, 10]): 120 | for iy, smallTopLeftY in enumerate([0, 3, 6]): 121 | if getWinner(board[(ix, iy)]) != None: 122 | continue 123 | 124 | for x in range(3): 125 | for y in range(3): 126 | canvasx = smallTopLeftX + (x * 2) 127 | canvasy = smallTopLeftY + y 128 | canvas[(canvasx, canvasy)] = board[(ix, iy)][(x, y)] 129 | 130 | # Print out the tic tac toe board: 131 | for y in range(9): 132 | for x in range(15): 133 | print(canvas[(x, y)], end="") 134 | if x == 4 or x == 9: 135 | print("|", end="") 136 | print() # Print a newline. 137 | 138 | if y == 2 or y == 5: 139 | print("-----+-----+-----") 140 | 141 | 142 | def getWinner(board): 143 | """Return X_PLAYER, O_PLAYER, or TIED depending on who won. Return 144 | None if there is no winner and the board isn't full yet.""" 145 | 146 | # Create short-named variables for the spaces on this board. 147 | topL, topM, topR = board[(0, 0)], board[(1, 0)], board[(2, 0)] 148 | midL, midM, midR = board[(0, 1)], board[(1, 1)], board[(2, 1)] 149 | botL, botM, botR = board[(0, 2)], board[(1, 2)], board[(2, 2)] 150 | 151 | for player in (X_PLAYER, O_PLAYER): 152 | if ( 153 | (topL == topM == topR == player) 154 | or (midL == midM == midR == player) # Top row 155 | or (botL == botM == botR == player) # Middle row 156 | or (topL == midL == botL == player) # Bottom row 157 | or (topM == midM == botM == player) # Left column 158 | or (topR == midR == botR == player) # Middle column 159 | or (topL == midM == botR == player) # Right column 160 | or (topR == midM == botL == player) # \ diagonal 161 | ): # / diagonal 162 | return player 163 | 164 | # Check for a tie: 165 | for x in range(BOARD_WIDTH): 166 | for y in range(BOARD_HEIGHT): 167 | if board[(x, y)] == EMPTY_SPACE: 168 | return None # Return None since there is no winner yet. 169 | return TIED 170 | 171 | 172 | def askForPlayerMove(player, board, focusX, focusY): 173 | """Asks the player which space on which small board to move on. 174 | The focusX and focusY values determine which small board the player 175 | can move on, but if they are both None the player can freely choose 176 | a small board. Returns the (x, y) of the small board the next player 177 | plays on. 178 | """ 179 | # Check if the player can freely select any small board: 180 | if focusX == None and focusY == None: 181 | # Let the player pick which board they want to move on: 182 | print(player + ": Enter the BOARD you want to move on.") 183 | validBoardsToSelect = [] 184 | for xyTuple, smallBoard in board.items(): 185 | if getWinner(smallBoard) == None: 186 | validBoardsToSelect.append(xyTuple) 187 | selectedBoard = enter1Through9(validBoardsToSelect) 188 | focusX = selectedBoard % 3 189 | focusY = selectedBoard // 3 190 | 191 | # Select the space on the focused small board: 192 | smallXDesc = ["left", "middle", "right"][focusX] 193 | smallYDesc = ["top", "middle", "bottom"][focusY] 194 | print(player, "moves on the", smallYDesc, smallXDesc, "board.") 195 | validSpacesToSelect = [] 196 | for xyTuple, tile in board[(focusX, focusY)].items(): 197 | if tile == EMPTY_SPACE: 198 | validSpacesToSelect.append(xyTuple) 199 | selectedSpace = enter1Through9(validSpacesToSelect) 200 | x = selectedSpace % 3 201 | y = selectedSpace // 3 202 | 203 | board[(focusX, focusY)][(x, y)] = player 204 | 205 | # Figure out the small board that the next player must move on: 206 | if getWinner(board[(x, y)]) == None: 207 | return (x, y) 208 | else: 209 | # If the small board has a winner or is tied, the next player 210 | # can move on any small board: 211 | return (None, None) 212 | 213 | 214 | def enter1Through9(validMoves): 215 | """Presents a "minimap" of a tic-tac-toe board's spaces, labeled 216 | with numbers 1 through 9. Returns the numeric space they chose. 217 | Valid moves is a list of (x, y) tuples representing the spaces 218 | the player can pick, e.g. [(0, 0), (0, 2)] means the player can 219 | only pick the top two corner spaces.""" 220 | for i, move in enumerate(validMoves): 221 | # Convert the (x, y) tuple values to an integer 1 through 9: 222 | validMoves[i] = str((move[1] * 3 + move[0]) + 1) 223 | print(" 1 2 3") 224 | print(" 4 5 6") 225 | print(" 7 8 9") 226 | print("Enter your move (1-9) or QUIT:") 227 | while True: # Keep asking the player until they enter a valid move. 228 | response = input("> ").upper() 229 | if response == "QUIT": 230 | print("Thanks for playing!") 231 | sys.exit() 232 | 233 | if response in validMoves: 234 | # Return a int that is 0-8, not a string that is 1-9. 235 | return int(response) - 1 236 | print("You cannot select that space.") 237 | 238 | 239 | def makeBoardFromSmallBoards(smallBoards): 240 | bigBoard = {} 241 | for x in range(BOARD_WIDTH): 242 | for y in range(BOARD_HEIGHT): 243 | winner = getWinner(smallBoards[(x, y)]) 244 | if winner == None: 245 | bigBoard[(x, y)] = EMPTY_SPACE 246 | elif winner == TIED: 247 | bigBoard[(x, y)] = TIED 248 | elif winner in (X_PLAYER, O_PLAYER): 249 | bigBoard[(x, y)] = winner 250 | return bigBoard 251 | 252 | 253 | # If the program is run (instead of imported), run the game: 254 | if __name__ == "__main__": 255 | main() 256 | -------------------------------------------------------------------------------- /quickpython/examples/towers.py: -------------------------------------------------------------------------------- 1 | """ 2 | /games/towers.py 3 | 4 | Copyright (c) 2019 ShineyDev 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | 19 | __authors__ = [("shineydev", "contact@shiney.dev")] 20 | __maintainers__ = [("shineydev", "contact@shiney.dev")] 21 | 22 | __version_info__ = (0, 0, 1, "alpha", 0) 23 | __version__ = "{0}.{1}.{2}{3}{4}".format( 24 | *[str(n)[0] if (i == 3) else str(n) for (i, n) in enumerate(__version_info__)] 25 | ) 26 | 27 | 28 | import os 29 | 30 | import colorama 31 | import pyfiglet 32 | 33 | HEIGHTS = {"easy": 3, "intermediate": 4, "hard": 5} 34 | 35 | 36 | class Towers: 37 | def __init__(self, difficulty: str): 38 | """ 39 | initializes a `Towers` object 40 | """ 41 | 42 | self.tower_height = HEIGHTS[difficulty] 43 | 44 | def game(self): 45 | """ 46 | starts the game 47 | """ 48 | 49 | pass 50 | 51 | def start(self): 52 | """ 53 | calls `self.game` in a 'would you like to play again?' loop 54 | """ 55 | 56 | choice = "y" 57 | while choice.startswith("y"): 58 | cls() 59 | print(pyfiglet.figlet_format("Towers")) 60 | print() 61 | input("enter to play\nctrl + c to quit to main menu\n\n") 62 | 63 | self.game() 64 | choice = input("\nwould you like to play again?\n> ").strip() 65 | 66 | 67 | if __name__ == "__main__": 68 | difficulty = None 69 | while difficulty not in {"easy", "intermediate", "hard"}: 70 | cls() 71 | print() 72 | difficulty = input("difficulty;\n> ").strip() 73 | 74 | game = Towers(difficulty) 75 | game.start() 76 | -------------------------------------------------------------------------------- /quickpython/examples/uno.py: -------------------------------------------------------------------------------- 1 | """ 2 | /games/uno.py 3 | 4 | Copyright (c) 2019 ShineyDev 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | 19 | __authors__ = [("shineydev", "contact@shiney.dev")] 20 | __maintainers__ = [("shineydev", "contact@shiney.dev")] 21 | 22 | __version_info__ = (2, 0, 0, "final", 0) 23 | __version__ = "{0}.{1}.{2}{3}{4}".format( 24 | *[str(n)[0] if (i == 3) else str(n) for (i, n) in enumerate(__version_info__)] 25 | ) 26 | 27 | 28 | import os 29 | import random 30 | 31 | import colorama 32 | import pyfiglet 33 | 34 | CARD_COLORS = ["Blue", "Green", "Red", "Yellow"] 35 | 36 | CARD_NAMES = [ 37 | "0", 38 | "1", 39 | "1", 40 | "2", 41 | "2", 42 | "3", 43 | "3", 44 | "4", 45 | "4", 46 | "5", 47 | "5", 48 | "6", 49 | "6", 50 | "7", 51 | "7", 52 | "8", 53 | "8", 54 | "9", 55 | "9", 56 | "Reverse", 57 | "Skip", 58 | "+2", 59 | ] 60 | 61 | START_HAND = 7 62 | 63 | COLOR_FORMATS = { 64 | "Blue": colorama.Fore.BLUE, 65 | "Green": colorama.Fore.GREEN, 66 | "Red": colorama.Fore.RED, 67 | "Yellow": colorama.Fore.YELLOW, 68 | } 69 | 70 | 71 | class Card: 72 | def __init__(self, color: str, name: str): 73 | """ 74 | initializes a `Card` object 75 | """ 76 | 77 | self.color = color 78 | self.name = name 79 | 80 | def format(self) -> str: 81 | """ 82 | formats the card with cli color codes 83 | 84 | returns: 85 | :: str :: the formatted card 86 | """ 87 | 88 | if self.color != "Wild": 89 | return "{}{}{}".format(COLOR_FORMATS[self.color], self.short_name, colorama.Fore.RESET) 90 | return self.short_name 91 | 92 | def is_wild(self) -> bool: 93 | """ 94 | checks whether the `Card` object is a wildcard 95 | 96 | returns: 97 | :: bool :: whether the `Card` object is a wildcard 98 | """ 99 | 100 | if self.color == "Wild": 101 | return True 102 | return False 103 | 104 | @property 105 | def short_name(self) -> str: 106 | """ 107 | generates a short name for the `Card` object 108 | 109 | returns: 110 | :: str :: the `Card` object's short name 111 | """ 112 | 113 | return "{}{}".format(self.color[:1:], self.name[:1:]) 114 | 115 | 116 | class Deck: 117 | def __init__(self): 118 | """ 119 | initializes a `Deck` object 120 | """ 121 | 122 | self.deck = list() 123 | self.in_play = list() 124 | self.in_pile = list() 125 | 126 | # add color cards 127 | for color in CARD_COLORS: 128 | for name in CARD_NAMES: 129 | self.deck.append(Card(color, name)) 130 | 131 | # add wildcards 132 | for i in range(4): 133 | for name in ["Change", "+4"]: 134 | self.deck.append(Card("Wild", name)) 135 | 136 | self.shuffle() 137 | 138 | def flip(self): 139 | """ 140 | flips `self.in_pile` back over into `self.deck` 141 | """ 142 | 143 | if not self.deck: 144 | self.deck = self.in_pile 145 | self.in_pile = list() 146 | 147 | def is_empty(self) -> bool: 148 | """ 149 | checks whether `self.deck` is empty 150 | 151 | returns: 152 | :: bool :: whether `self.deck` is empty 153 | """ 154 | 155 | if len(self.deck) == 0: 156 | return True 157 | return False 158 | 159 | def reset(self): 160 | """ 161 | re-builds `self.deck`, `self.in_play` and `self.in_pile` and calls `self.shuffle` 162 | """ 163 | 164 | self.deck = list() 165 | self.in_play = list() 166 | self.in_pile = list() 167 | 168 | for color in CARD_COLORS: 169 | for name in CARD_NAMES: 170 | self.deck.append(Card(color, name)) 171 | 172 | for i in range(4): 173 | for name in ["Change", "+4"]: 174 | self.deck.append(Card("Wild", name)) 175 | 176 | self.shuffle() 177 | 178 | def shuffle(self): 179 | """ 180 | shuffles `self.deck` 181 | """ 182 | 183 | random.shuffle(self.deck) 184 | 185 | 186 | class Hand: 187 | def __init__(self, deck: Deck): 188 | """ 189 | initializes a `Hand` object 190 | """ 191 | 192 | self.deck = deck 193 | self.hand = list() 194 | 195 | def __len__(self) -> int: 196 | """ 197 | utilized by built-in `len` function 198 | """ 199 | 200 | return len(self.hand) 201 | 202 | def generate(self): 203 | """ 204 | fills `self.hand` up to len( `START_HAND` ) 205 | """ 206 | 207 | for i in range(START_HAND): 208 | card = self.deck.deck.pop() 209 | self.hand.append(card) 210 | self.deck.in_play.append(card) 211 | 212 | def reset(self): 213 | """ 214 | sets `self.hand` to an empty list 215 | """ 216 | 217 | self.hand = list() 218 | 219 | 220 | class Player: 221 | def __init__(self, name: str, deck: Deck): 222 | """ 223 | initializes a `Player` object 224 | """ 225 | 226 | self.name = name 227 | self.deck = deck 228 | 229 | self.hand = Hand(self.deck) 230 | 231 | def is_winner(self) -> bool: 232 | """ 233 | checks whether the `Player` has won the game 234 | 235 | returns: 236 | :: bool :: whether the `Player` has won the game 237 | """ 238 | 239 | if len(self.hand) == 0: 240 | return True 241 | return False 242 | 243 | 244 | class Uno: 245 | def __init__(self, player_count: int): 246 | """ 247 | initializes an `Uno` object 248 | """ 249 | 250 | self.player_count = player_count 251 | 252 | def game(self): 253 | """ 254 | starts the game 255 | """ 256 | 257 | self.deck = Deck() 258 | self.players = list() 259 | 260 | self.current_player = 0 261 | self.clockwise = True 262 | 263 | self.message = "" 264 | self.color = None 265 | 266 | for i in range(self.player_count): 267 | self.players.append(Player("P{}".format(i + 1), self.deck)) 268 | self.players[i].hand.generate() 269 | 270 | card = self.deck.deck.pop() 271 | self.top_card = card 272 | self.deck.in_pile.append(card) 273 | 274 | if len(self.players) == 2: 275 | players = [self.players[0].name, self.players[1].name, "--", "--"] 276 | elif len(self.players) == 3: 277 | players = [ 278 | self.players[0].name, 279 | self.players[1].name, 280 | self.players[2].name, 281 | "--", 282 | ] 283 | elif len(self.players) == 4: 284 | players = [ 285 | self.players[0].name, 286 | self.players[1].name, 287 | self.players[2].name, 288 | self.players[3].name, 289 | ] 290 | 291 | while not any([player.is_winner() for player in self.players]): 292 | cls() 293 | 294 | if self.color: 295 | color = COLOR_FORMATS[self.color] 296 | else: 297 | color = "" 298 | 299 | if self.current_player == 0: 300 | pointers = ["/\\", " ", " ", " "] 301 | elif self.current_player == 1: 302 | pointers = [" ", ">", " ", " "] 303 | elif self.current_player == 2: 304 | pointers = [" ", " ", "\\/", " "] 305 | elif self.current_player == 3: 306 | pointers = [" ", " ", " ", "<"] 307 | 308 | print( 309 | """ 310 | {4} left in deck 311 | 312 | {0} 313 | {8} 314 | 315 | {3} {11} {5}{6}{7} {9} {1} 316 | 317 | {10} 318 | {2} 319 | """.format( 320 | *players, 321 | len(self.deck.deck), 322 | color, 323 | self.top_card.format(), 324 | colorama.Fore.RESET, 325 | *pointers 326 | ) 327 | ) 328 | 329 | print() 330 | 331 | if self.message: 332 | print(self.message) 333 | print() 334 | 335 | self.message = "" 336 | 337 | print(self.players[self.current_player].name) 338 | 339 | for card in self.players[self.current_player].hand.hand: 340 | print(card.format(), end=" ") 341 | 342 | print() 343 | print() 344 | 345 | card = input("choose a card to place;\n> ").strip().upper() 346 | 347 | if card == "+": 348 | if self.deck.is_empty(): 349 | self.deck.flip() 350 | 351 | card = self.deck.deck.pop() 352 | self.players[self.current_player].hand.hand.append(card) 353 | self.deck.in_play.append(card) 354 | continue 355 | 356 | try: 357 | card_index = int(card) 358 | 359 | if card_index not in range(1, len(self.players[self.current_player].hand.hand) + 1): 360 | self.message = ( 361 | "card must be an integer in range 1 - {0} or the card name".format( 362 | len(self.players[self.current_player].hand.hand) 363 | ) 364 | ) 365 | continue 366 | 367 | card = self.players[self.current_player].hand.hand[card_index - 1] 368 | except (ValueError) as e: 369 | if card not in [ 370 | card.short_name for card in self.players[self.current_player].hand.hand 371 | ]: 372 | self.message = ( 373 | "card must be an integer in range 1 - {0} or the card name".format( 374 | len(self.players[self.current_player].hand.hand) 375 | ) 376 | ) 377 | continue 378 | 379 | card = self.players[self.current_player].hand.hand[ 380 | [card.short_name for card in self.players[self.current_player].hand.hand].index( 381 | card 382 | ) 383 | ] 384 | 385 | if card.color == "Wild": 386 | pass 387 | elif self.top_card.color == "Wild": 388 | if card.color != self.color: 389 | self.message = "you can't place that card" 390 | continue 391 | elif card.color == self.top_card.color: 392 | pass 393 | elif card.name == self.top_card.name: 394 | pass 395 | else: 396 | self.message = "you can't place that card" 397 | continue 398 | 399 | card = self.players[self.current_player].hand.hand.pop( 400 | self.players[self.current_player].hand.hand.index(card) 401 | ) 402 | self.top_card = card 403 | self.deck.in_play.remove(card) 404 | self.deck.in_pile.append(card) 405 | 406 | add_cards = 0 407 | skip_player = False 408 | reverse = False 409 | 410 | color = "" 411 | 412 | if self.top_card.name == "Change": 413 | self.color = "" 414 | while self.color not in ["Blue", "Green", "Red", "Yellow"]: 415 | cls() 416 | 417 | print( 418 | """ 419 | {4} left in deck 420 | 421 | {0} 422 | {8} 423 | 424 | {3} {11} {5}{6}{7} {9} {1} 425 | 426 | {10} 427 | {2} 428 | """.format( 429 | *players, 430 | len(self.deck.deck), 431 | color, 432 | self.top_card.format(), 433 | colorama.Fore.RESET, 434 | *pointers 435 | ) 436 | ) 437 | 438 | print() 439 | 440 | if self.message: 441 | print(self.message) 442 | print() 443 | 444 | self.message = "" 445 | 446 | print(self.players[self.current_player].name) 447 | 448 | for card in self.players[self.current_player].hand.hand: 449 | print(card.format(), end=" ") 450 | 451 | print() 452 | print() 453 | 454 | self.color = input("choose a color;\n> ").strip().capitalize() 455 | elif self.top_card.name == "+4": 456 | self.color = "" 457 | while self.color not in ["Blue", "Green", "Red", "Yellow"]: 458 | cls() 459 | 460 | print( 461 | """ 462 | {4} left in deck 463 | 464 | {0} 465 | {8} 466 | 467 | {3} {11} {5}{6}{7} {9} {1} 468 | 469 | {10} 470 | {2} 471 | """.format( 472 | *players, 473 | len(self.deck.deck), 474 | color, 475 | self.top_card.format(), 476 | colorama.Fore.RESET, 477 | *pointers 478 | ) 479 | ) 480 | 481 | print() 482 | 483 | if self.message: 484 | print(self.message) 485 | print() 486 | 487 | self.message = "" 488 | 489 | print(self.players[self.current_player].name) 490 | 491 | for card in self.players[self.current_player].hand.hand: 492 | print(card.format(), end=" ") 493 | 494 | print() 495 | print() 496 | 497 | self.color = input("choose a color;\n> ").strip().capitalize() 498 | 499 | add_cards = 4 500 | skip_player = True 501 | elif self.top_card.name == "+2": 502 | add_cards = 2 503 | skip_player = True 504 | elif self.top_card.name == "Skip": 505 | skip_player = True 506 | elif self.top_card.name == "Reverse": 507 | reverse = True 508 | 509 | if reverse: 510 | self.clockwise = not self.clockwise 511 | 512 | if len(self.players) == 2: 513 | # we don't want to change the player 514 | continue 515 | 516 | if self.clockwise: 517 | next_player = self.current_player + 1 518 | else: 519 | next_player = self.current_player - 1 520 | 521 | if next_player > (len(self.players) - 1): 522 | next_player -= len(self.players) 523 | elif next_player < 0: 524 | next_player += len(self.players) 525 | 526 | if add_cards: 527 | for i in range(add_cards): 528 | if self.deck.is_empty(): 529 | self.deck.flip() 530 | 531 | card = self.deck.deck.pop() 532 | self.players[next_player].hand.hand.append(card) 533 | self.deck.in_play.append(card) 534 | 535 | if skip_player: 536 | if self.clockwise: 537 | next_player = self.current_player + 2 538 | else: 539 | next_player = self.current_player - 2 540 | 541 | if next_player > (len(self.players) - 1): 542 | next_player -= len(self.players) 543 | elif next_player < 0: 544 | next_player += len(self.players) 545 | 546 | self.current_player = next_player 547 | 548 | winner = [player for player in self.players if player.is_winner()][0] 549 | 550 | cls() 551 | 552 | print() 553 | print( 554 | """ 555 | {4} left in deck 556 | 557 | {0} 558 | {8} 559 | 560 | {3} {11} {5}{6}{7} {9} {1} 561 | 562 | {10} 563 | {2} 564 | """.format( 565 | *players, 566 | len(self.deck.deck), 567 | color, 568 | self.top_card.format(), 569 | colorama.Fore.RESET, 570 | *pointers 571 | ) 572 | ) 573 | 574 | print() 575 | print("{0} wins!".format(winner.name)) 576 | 577 | def start(self): 578 | """ 579 | calls `self.game` in a 'would you like to play again?' loop 580 | """ 581 | 582 | choice = "y" 583 | while choice.startswith("y"): 584 | cls() 585 | print(pyfiglet.figlet_format("Uno")) 586 | print() 587 | input("enter to play\nctrl + c to quit to main menu\n\n") 588 | 589 | self.game() 590 | choice = input("\nwould you like to play again?\n> ").strip() 591 | 592 | 593 | if __name__ == "__main__": 594 | players = None 595 | while not isinstance(players, int): 596 | cls() 597 | players = input("players;\n> ") 598 | 599 | try: 600 | players = int(players) 601 | except (ValueError) as e: 602 | pass 603 | 604 | game = Uno(players) 605 | game.start() 606 | -------------------------------------------------------------------------------- /quickpython/examples/zigzag.py: -------------------------------------------------------------------------------- 1 | """Zigzag, by Al Sweigart al@inventwithpython.com 2 | A simple zig zag animation. Press Ctrl-C to stop. 3 | This and other games are available at https://nostarch.com/XX 4 | Tags: tiny, beginner, artistic, scrolling""" 5 | __version__ = 0 6 | import sys 7 | import time 8 | 9 | if __name__ == "__main__": 10 | print("Zigzag, by Al Sweigart al@inventwithpython.com") 11 | print("Press Ctrl-C to quit.") 12 | time.sleep(3) 13 | 14 | indentSize = 0 # How many spaces to indent. 15 | 16 | try: 17 | while True: # The main program loop. 18 | # Zig to the right 20 times: 19 | for i in range(20): 20 | indentSize = indentSize + 1 21 | indentation = " " * indentSize 22 | print(" " * indentSize + "********") 23 | time.sleep(0.05) # Pause for 50 milliseconds. 24 | 25 | # Zag to the left 20 times: 26 | for i in range(20): 27 | indentSize = indentSize - 1 28 | indentation = " " * indentSize 29 | print(indentation + "********") 30 | time.sleep(0.05) # Pause for 50 milliseconds. 31 | except KeyboardInterrupt: 32 | sys.exit() # When Ctrl-C is pressed, end the program. 33 | -------------------------------------------------------------------------------- /quickpython/extensions.py: -------------------------------------------------------------------------------- 1 | import builtins 2 | import inspect 3 | import platform 4 | from subprocess import run 5 | 6 | 7 | def beep(): 8 | """Makes a beep sound.""" 9 | print("\a", end="") 10 | 11 | 12 | def cls(): 13 | """Clears the screen.""" 14 | if platform.system().lower() == "windows": 15 | run("cls") 16 | else: 17 | run("clear") 18 | 19 | 20 | def main(function): 21 | """A decorator that causes the decorated function to be started 22 | if ran directly but not if imported. 23 | """ 24 | caller = inspect.stack()[1] 25 | module = inspect.getmodule(caller[0]) 26 | if module.__name__ == "__main__": 27 | function() 28 | return function 29 | 30 | 31 | setattr(builtins, "beep", beep) # noqa 32 | setattr(builtins, "cls", cls) # noqa 33 | setattr(builtins, "main", main) # noqa 34 | -------------------------------------------------------------------------------- /scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | poetry run isort quickpython/ tests/ 5 | poetry run black quickpython/ tests/ 6 | -------------------------------------------------------------------------------- /scripts/done.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | ./scripts/clean.sh 5 | ./scripts/test.sh 6 | -------------------------------------------------------------------------------- /scripts/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | poetry run cruft check 5 | poetry run mypy --ignore-missing-imports quickpython/ 6 | poetry run isort --check --diff quickpython/ tests/ 7 | poetry run black --check quickpython/ tests/ 8 | poetry run flake8 quickpython/ tests/ 9 | poetry run safety check -i 39462 10 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | ./scripts/lint.sh 5 | poetry run pytest -s --cov=quickpython/ --cov=tests --cov-report=term-missing ${@-} --cov-report html 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 100 3 | extend-ignore = 4 | E203 # https://github.com/psf/black/blob/master/docs/the_black_code_style.md#slices 5 | exclude = 6 | quickpython/examples/* 7 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/quickpython/adbf79947f9c9e094b5a32bf72c056bfcc7c623d/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_importable.py: -------------------------------------------------------------------------------- 1 | """A basic import test""" 2 | 3 | 4 | def test_can_import(): 5 | import quickpython 6 | 7 | assert quickpython 8 | --------------------------------------------------------------------------------