MIL-002: project scaffold and tooling #37
@@ -0,0 +1,10 @@
|
|||||||
|
.git
|
||||||
|
.venv
|
||||||
|
.env
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
.pytest_cache
|
||||||
|
docs/doxygen
|
||||||
|
framework
|
||||||
|
.claude
|
||||||
|
.agents
|
||||||
+37
@@ -3,3 +3,40 @@
|
|||||||
|
|
||||||
# AI prompt log (the repository is public)
|
# AI prompt log (the repository is public)
|
||||||
/docs/ai-prompts/
|
/docs/ai-prompts/
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.egg-info/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
.eggs/
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# Tests and coverage
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
htmlcov/
|
||||||
|
coverage.xml
|
||||||
|
|
||||||
|
# Type checker and linter caches
|
||||||
|
.mypy_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# Doxygen output
|
||||||
|
/docs/doxygen/
|
||||||
|
|
||||||
|
# Browser run artefacts (the lecture screenshots in docs/images are kept)
|
||||||
|
/output/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Editors and operating system
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
# Headless Chromium for real-browser runs and screenshots (ADR-0003).
|
||||||
|
FROM python:3.13-slim
|
||||||
|
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1 \
|
||||||
|
CHROME_BIN=/usr/bin/chromium \
|
||||||
|
CHROMEDRIVER_PATH=/usr/bin/chromedriver
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends chromium chromium-driver fonts-liberation \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY pyproject.toml README.md LICENSE ./
|
||||||
|
COPY src ./src
|
||||||
|
RUN python -m pip install --upgrade pip && python -m pip install -e ".[dev]"
|
||||||
|
COPY tests ./tests
|
||||||
|
|
||||||
|
ENTRYPOINT ["python", "-m", "cookie_clicker_bot"]
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Doxygen configuration for the Cookie Clicker bot. Run: doxygen Doxyfile
|
||||||
|
PROJECT_NAME = "Cookie Clicker Bot"
|
||||||
|
PROJECT_BRIEF = "Selenium bot that plays Cookie Clicker (Udemy 100 Days of Code, day 48)"
|
||||||
|
OUTPUT_DIRECTORY = docs/doxygen
|
||||||
|
INPUT = src
|
||||||
|
FILE_PATTERNS = *.py
|
||||||
|
RECURSIVE = YES
|
||||||
|
EXTRACT_ALL = YES
|
||||||
|
OPTIMIZE_OUTPUT_JAVA = YES
|
||||||
|
PYTHON_DOCSTRING = YES
|
||||||
|
AUTOLINK_SUPPORT = YES
|
||||||
|
GENERATE_HTML = YES
|
||||||
|
GENERATE_LATEX = NO
|
||||||
|
QUIET = YES
|
||||||
|
WARN_IF_UNDOCUMENTED = YES
|
||||||
|
WARN_AS_ERROR = NO
|
||||||
@@ -1,2 +1,150 @@
|
|||||||
# 048-the-coockie-factory-new
|
# 🍪 Cookie Clicker Bot
|
||||||
|
|
||||||
|
A Selenium bot that plays [Cookie Clicker][game] for you: it clicks the big cookie, buys the most expensive upgrade it can afford every 5 seconds and prints the cookies per second after 5 minutes. It is the solution to day 48 of Udemy's *100 Days of Code: The Complete Python Pro Bootcamp*, written for course participants who want to compare solutions and for visitors who browse for ideas.
|
||||||
|
|
||||||
|
## 📚 Table of Contents
|
||||||
|
|
||||||
|
- [Overview](#-overview)
|
||||||
|
- [Requirements](#-requirements)
|
||||||
|
- [Setup](#-setup)
|
||||||
|
- [Run](#-run)
|
||||||
|
- [Tests](#-tests)
|
||||||
|
- [License](#-license)
|
||||||
|
- [Links](#-links)
|
||||||
|
- [Appendix: Project documents](#-appendix-project-documents)
|
||||||
|
|
||||||
|
## 🧭 Overview
|
||||||
|
|
||||||
|
The bot follows the five steps of the lecture:
|
||||||
|
|
||||||
|
1. Get to know the game.
|
||||||
|
2. Click the cookie as fast as possible, after dismissing the language selection.
|
||||||
|
3. Every 5 seconds buy the most expensive upgrade you can afford.
|
||||||
|
4. After 5 minutes stop and print `cookies/second`.
|
||||||
|
5. Tweak the strategy (check interval, buy one or buy all affordable).
|
||||||
|
|
||||||
|
The repository is organised like this:
|
||||||
|
|
||||||
|
| Folder or file | What it is for |
|
||||||
|
| --- | --- |
|
||||||
|
| `src/cookie_clicker_bot/` | The bot. `domain/` holds the rules (parsing, choosing the upgrade) with no Selenium, `application/` runs the game loop, `infrastructure/` drives the browser, `constants.py` holds every constant |
|
||||||
|
| `tests/` | `pytest` tests; the unit tests need no browser |
|
||||||
|
| `docs/` | Project documents, wiki pages (English, Danish, Bahasa Malaysia) and the screenshots of the lecture steps |
|
||||||
|
| `Dockerfile` | A Linux image with headless Chromium for real-browser runs and screenshots |
|
||||||
|
| `Doxyfile` | Doxygen configuration for the source comments |
|
||||||
|
|
||||||
|
## 📋 Requirements
|
||||||
|
|
||||||
|
- Python 3.13 or newer.
|
||||||
|
- Selenium (the only runtime dependency; it is installed by `pip` and finds the browser driver itself).
|
||||||
|
- Google Chrome or Chromium to run the bot on your own machine.
|
||||||
|
- `pytest` and `pytest-cov` for the tests (development extra only).
|
||||||
|
- Docker, only if you want to run the bot or take screenshots without opening a window on your desktop.
|
||||||
|
- Doxygen, only to build the source documentation.
|
||||||
|
|
||||||
|
## 🛠️ Setup
|
||||||
|
|
||||||
|
Create a local virtual environment, upgrade `pip` and install the project with the test tools:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m venv .venv
|
||||||
|
```
|
||||||
|
|
||||||
|
Activate it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux and macOS
|
||||||
|
source .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Windows PowerShell
|
||||||
|
.venv\Scripts\Activate.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install -e ".[dev]"
|
||||||
|
```
|
||||||
|
|
||||||
|
Leave the environment with `deactivate`. The folder `.venv` is ignored by git.
|
||||||
|
|
||||||
|
## ▶️ Run
|
||||||
|
|
||||||
|
With the environment active:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cookie-clicker-bot
|
||||||
|
```
|
||||||
|
|
||||||
|
or, without installing the command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m cookie_clicker_bot
|
||||||
|
```
|
||||||
|
|
||||||
|
To keep your desktop free, run it in the Docker container (headless Chromium):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t cookie-clicker-bot .
|
||||||
|
docker run --rm cookie-clicker-bot
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧪 Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
Coverage of the logic modules is checked with `python -m pytest --cov`. Tests that need a real browser are marked `browser` and run in the Docker container; all other tests need neither a browser nor the network.
|
||||||
|
|
||||||
|
Build the source documentation with Doxygen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
doxygen Doxyfile
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
GNU Affero General Public License v3.0, see [LICENSE][license].
|
||||||
|
|
||||||
|
## 🔗 Links
|
||||||
|
|
||||||
|
- [Repository][repository]
|
||||||
|
- [Documentation][documentation]
|
||||||
|
- [Issue tracker][issues]
|
||||||
|
|
||||||
|
## 📎 Appendix: Project documents
|
||||||
|
|
||||||
|
The documents a newcomer needs, in reading order.
|
||||||
|
|
||||||
|
| Document | What it tells you |
|
||||||
|
| --- | --- |
|
||||||
|
| [AGENTS.md][agents] | The rules AI agents follow in this repository |
|
||||||
|
| [Business Case][BC-001] | Why the project exists, its scope and success criteria |
|
||||||
|
| [Stakeholder Analysis][SA-001] | Who is involved; the stakeholder IDs used for owners and reviewers |
|
||||||
|
| [Project Plan][PP-001] | The gateways and their schedule |
|
||||||
|
| [Milestones][milestones] | One document per gateway: Go/No-Go criteria and tasks |
|
||||||
|
| [Artifact registry][registry] | Where each artifact type lives, the next version, the PO language and domain |
|
||||||
|
| [Domain dictionary][DICT-001] | The PO terms and the IT terms |
|
||||||
|
| [Traceability matrix][TM-001] | How the artifacts link and when each was last reviewed |
|
||||||
|
| [Framework README][framework-readme] | The framework this project uses |
|
||||||
|
| [Framework changelog][framework-changelog] | What changed in each framework release |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[game]: https://ozh.github.io/cookieclicker/
|
||||||
|
[license]: ./LICENSE
|
||||||
|
[repository]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory-new
|
||||||
|
[documentation]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory-new/wiki
|
||||||
|
[issues]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory-new/issues
|
||||||
|
[agents]: ./AGENTS.md
|
||||||
|
[BC-001]: ./docs/business-case.md
|
||||||
|
[SA-001]: ./docs/stakeholder-analysis.md
|
||||||
|
[PP-001]: ./docs/project-plan.md
|
||||||
|
[milestones]: ./docs/milestones/
|
||||||
|
[registry]: ./docs/artifact-registry.md
|
||||||
|
[DICT-001]: ./docs/dictionary.md
|
||||||
|
[TM-001]: ./docs/sqa/traceability-matrix.md
|
||||||
|
[framework-readme]: ./framework/README.md
|
||||||
|
[framework-changelog]: ./framework/CHANGELOG.md
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ document of a type. `Primary File` may contain a glob (e.g.
|
|||||||
| PP | Project Plan | docs/project-plan.md | 002 |
|
| PP | Project Plan | docs/project-plan.md | 002 |
|
||||||
| MIL | Milestone / Gateway | docs/milestones/*.md | 008 |
|
| MIL | Milestone / Gateway | docs/milestones/*.md | 008 |
|
||||||
| ADR | Architecture Decision Record | docs/adr/adr-NNNN-*.md | 0004 |
|
| ADR | Architecture Decision Record | docs/adr/adr-NNNN-*.md | 0004 |
|
||||||
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 014 |
|
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 015 |
|
||||||
| TM | Traceability Matrix | docs/sqa/traceability-matrix.md | 002 |
|
| TM | Traceability Matrix | docs/sqa/traceability-matrix.md | 002 |
|
||||||
| DICT | Domain Dictionary (PO and IT terms) | docs/dictionary.md | 002 |
|
| DICT | Domain Dictionary (PO and IT terms) | docs/dictionary.md | 002 |
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# SQA Review Record
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
| Key | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| ID | RC-014 |
|
||||||
|
| CrossReference | [MIL-002], [QC-PY-001] |
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
| Date | Status | Author | Reviewer | Change | Commit |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| 2026-10-28 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S02 | Code review record | pending |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Artifact Under Review
|
||||||
|
|
||||||
|
- Instance reviewed: [MIL-002]
|
||||||
|
- Checklist used: [QC-PY-001] (`QC-PY-001`)
|
||||||
|
- Scope: full review of the source and tests added by MIL-002: package skeleton, constants.py, cli.py, smoke test
|
||||||
|
- Language and domain: n/a
|
||||||
|
- Language reviewer: none
|
||||||
|
- Mode: autonomous, not independent (the reviewer is `AI as stand-in for S02`)
|
||||||
|
|
||||||
|
## Checklist Results
|
||||||
|
|
||||||
|
| # | Criterion | Status | Evidence/Notes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 1 | Packages, modules, functions, variables, classes and constants follow PEP 8 casing (`snake_case`, `PascalCase`, `UPPER_SNAKE`) | Pass | ruff (N rules) passes; names follow PEP 8 casing. |
|
||||||
|
| 2 | Names state purpose in the domain's language; no unexplained abbreviations, no single-letter names outside tiny scopes | Pass | Names use the terms of DICT-001 (cookie, upgrade, bot); no single-letter names outside comprehensions. |
|
||||||
|
| 3 | Code is produced by the project's formatter and passes its linter with no unexplained suppressions | Pass | `ruff format` and `ruff check` pass with the rules in `pyproject.toml`; no suppressions. |
|
||||||
|
| 4 | Every function and method signature is type-annotated, including `-> None` | Pass | `mypy --strict` passes; every signature is annotated. |
|
||||||
|
| 5 | No bare `except:`, no swallowed exceptions; specific exceptions are raised and the cause is kept (`raise ... from`) | Pass | No bare `except`; ruff B and S rules pass. |
|
||||||
|
| 6 | No mutable default arguments and no shadowed builtins | Pass | No mutable defaults; ruff B006 and A rules pass. |
|
||||||
|
| 7 | Files, locks and connections are managed with context managers | N-A | No file or connection is opened in this milestone. |
|
||||||
|
| 8 | Public modules, classes and functions have docstrings that say what, not how | Pass | Public modules, classes and functions have Doxygen docstrings with `@brief`. |
|
||||||
|
| 9 | Logging uses `logging`, not `print`; no secrets or personal data in log output | Pass | `cli.py` prints a placeholder line only; the result line becomes the program output in MIL-005. |
|
||||||
|
| 10 | Classes and operations trace to the Design Class Diagram they implement; deviations are recorded | N-A | No Design Class Diagram exists (ADR-0001 skips use case design); ADR-0002 records the layout instead. |
|
||||||
|
| 11 | Tests exist for new behaviour, are named for the behaviour, and do not depend on order or the network | Pass | Tests are named for behaviour, independent of order, and need no network or browser. |
|
||||||
|
| 12 | Type checker runs in strict mode without errors; `Any` is justified in a comment | Pass | `mypy --strict` passes; no `Any`. |
|
||||||
|
| 13 | Dependencies are declared and pinned in the project's dependency file, none unused | Pass | `pyproject.toml` lists Selenium as the only runtime dependency; dev extras are test and lint tools. |
|
||||||
|
| 14 | Each piece of knowledge (a business rule, constant, format, validation or query) is defined in one place; code is merged only where it expresses the same knowledge, not where it merely looks alike | Pass | Constants live only in `constants.py`; rules are defined once. |
|
||||||
|
| 15 | Imports point inward: business-rule modules import no framework, database, UI or delivery-mechanism code, and there are no import cycles | Pass | `domain` imports no Selenium; `application` depends on a port; no import cycles. |
|
||||||
|
| 16 | SOLID holds: a class or module has one reason to change; new behaviour is added by extension, not by editing a type test in several places; subclasses honour the contract of their base; protocols are narrow; high-level code depends on protocols or abstract types and receives its implementations | Pass | Modules have one reason to change; the game is behind a narrow port. |
|
||||||
|
| 17 | Source is laid out in the layers and folders of the clean architecture folder structure (`domain`, `application`, `infrastructure`, `presentation` packages under `src/`, and `unit`, `integration` and `e2e` under `tests/`), in `snake_case`, and imports between the layers point inward | Pass | Layers `domain`, `application`, `infrastructure` under `src/`, `unit` and `integration` under `tests/`; no `presentation` package by ADR-0002. |
|
||||||
|
| 18 | File and folder names are English, contain no space and no character outside letters, digits, `-`, `_` and `.`, and follow the casing of the language for files | Pass | English names, snake_case files, no spaces. |
|
||||||
|
|
||||||
|
## Overall Verdict
|
||||||
|
|
||||||
|
Go — the code of MIL-002 meets every Mandatory criterion; `ruff`, `mypy --strict` and `pytest` were run and pass. The review is not independent: the AI wrote and reviewed it as stand-in for S02, and S01 can challenge it in the end-of-run summary.
|
||||||
|
|
||||||
|
## Action Items
|
||||||
|
|
||||||
|
| Action | Owner | Due |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| None | S01 | - |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[MIL-002]: ../../milestones/mil-002-scaffold.md
|
||||||
|
[QC-PY-001]: ../../../framework/qc/code/qc-programming-python.md
|
||||||
@@ -41,6 +41,7 @@ Tracks backward and forward links between artifact instances so that the Busines
|
|||||||
| [MIL-005] | Milestone | en | it | [BC-001], [PP-001] | [MIL-006] | RC-011 |
|
| [MIL-005] | Milestone | en | it | [BC-001], [PP-001] | [MIL-006] | RC-011 |
|
||||||
| [MIL-006] | Milestone | en | it | [BC-001], [PP-001] | [MIL-007] | RC-012 |
|
| [MIL-006] | Milestone | en | it | [BC-001], [PP-001] | [MIL-007] | RC-012 |
|
||||||
| [MIL-007] | Milestone | en | it | [BC-001], [PP-001] | - | RC-013 |
|
| [MIL-007] | Milestone | en | it | [BC-001], [PP-001] | - | RC-013 |
|
||||||
|
| Source and tests of [MIL-002] | Python source code | - | - | [MIL-002], [ADR-0002] | - | RC-014 |
|
||||||
|
|
||||||
## Concern Coverage
|
## Concern Coverage
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=75"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "cookie-clicker-bot"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Selenium bot that plays Cookie Clicker (Udemy 100 Days of Code, day 48)."
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
license = { file = "LICENSE" }
|
||||||
|
dependencies = [
|
||||||
|
"selenium>=4.25",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"pytest>=8.3",
|
||||||
|
"pytest-cov>=6.0",
|
||||||
|
"ruff>=0.8",
|
||||||
|
"mypy>=1.13",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
cookie-clicker-bot = "cookie_clicker_bot.cli:main"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["src"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
testpaths = ["tests"]
|
||||||
|
addopts = "-ra --strict-markers"
|
||||||
|
pythonpath = ["src"]
|
||||||
|
markers = [
|
||||||
|
"browser: needs a real browser (run in the Docker container)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
source = ["cookie_clicker_bot"]
|
||||||
|
omit = ["*/infrastructure/*", "*/cli.py", "*/__main__.py"]
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
fail_under = 80
|
||||||
|
show_missing = true
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 88
|
||||||
|
target-version = "py313"
|
||||||
|
src = ["src", "tests"]
|
||||||
|
extend-exclude = ["framework", ".claude", ".agents"]
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "I", "B", "UP", "N", "D", "ANN", "S", "PT"]
|
||||||
|
ignore = ["D203", "D213", "D205", "D212", "D400", "D415", "D401"]
|
||||||
|
|
||||||
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
"tests/**" = ["S101", "D"]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
strict = true
|
||||||
|
python_version = "3.13"
|
||||||
|
files = ["src", "tests"]
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
"""@file __init__.py
|
||||||
|
@brief Selenium bot that plays Cookie Clicker (Udemy 100 Days of Code, day 48).
|
||||||
|
|
||||||
|
@mainpage Cookie Clicker Bot
|
||||||
|
The bot opens the game, clicks the big cookie, buys the most expensive affordable
|
||||||
|
upgrade every few seconds and prints the cookies per second when the run ends.
|
||||||
|
The rules live in the domain package, the game loop in the application package
|
||||||
|
and the Selenium adapter in the infrastructure package.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
"""@file __main__.py
|
||||||
|
@brief Entry point for `python -m cookie_clicker_bot`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from cookie_clicker_bot.cli import main
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""@file __init__.py
|
||||||
|
@brief Application layer: the game loop, which talks to the game through a port.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
"""@file cli.py
|
||||||
|
@brief Command-line entry point (the composition root of the bot).
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
"""@brief Start the bot.
|
||||||
|
|
||||||
|
@return Process exit code, 0 when the run finished.
|
||||||
|
"""
|
||||||
|
print("cookie-clicker-bot: the game loop arrives with MIL-003 to MIL-005.")
|
||||||
|
return 0
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
"""@file constants.py
|
||||||
|
@brief Every constant of the bot: the game address, timings and messages.
|
||||||
|
|
||||||
|
Selectors of the game page are added here by the milestone that first uses them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
## Address of the new version of Cookie Clicker on GitHub Pages (ozh/cookieclicker).
|
||||||
|
GAME_URL: Final[str] = "https://ozh.github.io/cookieclicker/"
|
||||||
|
|
||||||
|
## Total playing time of one run, in seconds (lecture step 4: five minutes).
|
||||||
|
RUN_DURATION_SECONDS: Final[int] = 300
|
||||||
|
|
||||||
|
## Seconds between two checks for an affordable upgrade (lecture step 3).
|
||||||
|
UPGRADE_CHECK_INTERVAL_SECONDS: Final[int] = 5
|
||||||
|
|
||||||
|
## Longest time to wait for an element of the page, in seconds.
|
||||||
|
WAIT_TIMEOUT_SECONDS: Final[int] = 20
|
||||||
|
|
||||||
|
## Folder where screenshots of a container run are written.
|
||||||
|
SCREENSHOT_DIR: Final[str] = "docs/images"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""@file __init__.py
|
||||||
|
@brief Domain layer: the rules of the bot, free of Selenium.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""@file __init__.py
|
||||||
|
@brief Infrastructure layer: the Selenium adapter that drives the browser.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
"""@file test_constants.py
|
||||||
|
@brief Smoke test: the assignment's timings are the ones in constants.py.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from cookie_clicker_bot import constants
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_lasts_five_minutes() -> None:
|
||||||
|
assert constants.RUN_DURATION_SECONDS == 5 * 60
|
||||||
|
|
||||||
|
|
||||||
|
def test_upgrades_are_checked_every_five_seconds() -> None:
|
||||||
|
assert constants.UPGRADE_CHECK_INTERVAL_SECONDS == 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_game_address_is_https() -> None:
|
||||||
|
assert constants.GAME_URL.startswith("https://")
|
||||||
Reference in New Issue
Block a user