MIL-005: Wiki in three languages and release #33

Merged
Tirsvad merged 1 commits from mil-005-wiki-release into main 2026-10-28 10:03:53 +01:00
30 changed files with 801 additions and 2 deletions
+38
View File
@@ -0,0 +1,38 @@
# Publish the project's wiki pages to the git host's wiki.
#
# On a push to main that changes docs/wiki/, this workflow calls the reusable
# workflow of TirSystem/github-action, which copies the Markdown pages of
# docs/wiki/ to the wiki of this repository (framework/process/wiki.md says
# what the folder holds: English and the PO language as a minimum).
#
# INSTALLING THIS FILE IS THE OPT-IN. There is no marker file. A project that
# does not copy it publishes nothing.
#
# Copy it to .gitea/workflows/publish-wiki.yml. Then:
# 1. Create the secret TOKEN_FOR_GITEA_WIKI in the organization of the
# repository: a Gitea token with scope write:repository for a user that
# can write this repository (see guides/credentials.md of github-action).
# 2. Enable the wiki in the repository settings.
# 3. Keep @v1, or pin the exact release tag together with tools-ref.
# A runner must be registered for the repository (or its organization).
#
# prune: "true" deletes wiki pages that are not in docs/wiki/, so the wiki
# always equals the folder. Leave it "false" to keep hand-written pages.
name: Publish wiki
on:
push:
branches: [ main ]
paths:
- "docs/wiki/**"
workflow_dispatch:
jobs:
wiki:
uses: TirSystem/git-workflows/.gitea/workflows/publish-wiki-pages.yml@v1
with:
wiki_dir: docs/wiki
prune: "true"
secrets:
TOKEN_FOR_GITEA_WIKI: ${{ secrets.TOKEN_FOR_GITEA_WIKI }}
+2 -2
View File
@@ -130,7 +130,7 @@ WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = src README.md
INPUT = src
INPUT_ENCODING = UTF-8
INPUT_FILE_ENCODING =
FILE_PATTERNS = *.py
@@ -147,7 +147,7 @@ INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = README.md
USE_MDFILE_AS_MAINPAGE =
IMPLICIT_DIR_DOCS = YES
FORTRAN_COMMENT_AFTER = 72
#---------------------------------------------------------------------------
+17
View File
@@ -0,0 +1,17 @@
# Appendiks
🌐 [English](Appendix-Documentation) · **Dansk** · [Bahasa Melayu](Appendix-Documentation-ms)
Projektets dokumenter i læserækkefølge. De ligger i repositoryet under `docs/`.
| Dokument | Hvad det fortæller dig |
| --- | --- |
| [Business Case](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/business-case.md) | Hvorfor projektet findes, dets afgrænsning og succeskriterier |
| [Stakeholder Analysis](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/stakeholder-analysis.md) | Hvem der er involveret; stakeholder-ID'erne |
| [Project Plan](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/project-plan.md) | Gateways og deres tidsplan |
| [Milestones](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/milestones/) | Ét dokument pr. gateway med dets opgaver |
| [ADR-0001](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/adr/adr-0001-selenium-runtime-dependency.md) | Hvorfor Selenium er den ene runtime-afhængighed |
| [Traceability matrix](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/sqa/traceability-matrix.md) | Hvordan artefakterne hænger sammen, og hvornår hvert sidst blev gennemgået |
| [Review records](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/sqa/reviews/) | Gennemgangsposterne |
[README](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory) · [AGENTS.md](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/AGENTS.md)
+17
View File
@@ -0,0 +1,17 @@
# Lampiran
🌐 [English](Appendix-Documentation) · [Dansk](Appendix-Documentation-da) · **Bahasa Melayu**
Dokumen projek mengikut susunan bacaan. Ia berada dalam repositori di bawah `docs/`.
| Dokumen | Apa yang diberitahunya |
| --- | --- |
| [Business Case](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/business-case.md) | Mengapa projek wujud, skop dan kriteria kejayaannya |
| [Stakeholder Analysis](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/stakeholder-analysis.md) | Siapa yang terlibat; ID pihak berkepentingan |
| [Project Plan](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/project-plan.md) | Gerbang dan jadualnya |
| [Milestones](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/milestones/) | Satu dokumen bagi setiap gerbang dengan tugasnya |
| [ADR-0001](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/adr/adr-0001-selenium-runtime-dependency.md) | Mengapa Selenium ialah satu-satunya kebergantungan masa jalan |
| [Traceability matrix](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/sqa/traceability-matrix.md) | Bagaimana artifak dipaut dan bila setiap satu disemak kali terakhir |
| [Review records](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/sqa/reviews/) | Rekod semakan |
[README](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory) · [AGENTS.md](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/AGENTS.md)
+17
View File
@@ -0,0 +1,17 @@
# Appendix
🌐 **English** · [Dansk](Appendix-Documentation-da) · [Bahasa Melayu](Appendix-Documentation-ms)
The project documents, in reading order. They live in the repository under `docs/`.
| Document | What it tells you |
| --- | --- |
| [Business Case](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/business-case.md) | Why the project exists, its scope and success criteria |
| [Stakeholder Analysis](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/stakeholder-analysis.md) | Who is involved; the stakeholder IDs |
| [Project Plan](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/project-plan.md) | The gateways and their schedule |
| [Milestones](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/milestones/) | One document per gateway with its tasks |
| [ADR-0001](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/adr/adr-0001-selenium-runtime-dependency.md) | Why Selenium is the one runtime dependency |
| [Traceability matrix](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/sqa/traceability-matrix.md) | How the artifacts link and when each was last reviewed |
| [Review records](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/docs/sqa/reviews/) | The review records |
[README](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory) · [AGENTS.md](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/src/branch/main/AGENTS.md)
+28
View File
@@ -0,0 +1,28 @@
# Beskrivelse
🌐 [English](Description) · **Dansk** · [Bahasa Melayu](Description-ms)
## Hvad projektet er
Botten åbner Cookie Clicker i Chrome, klikker på den store cookie så hurtigt spillet tillader, besøger butikken hvert femte sekund og køber det dyreste produkt, den har råd til. Efter 300 sekunder aflæser den cookies i sekundet og skriver `Result : <cps>`.
## Hvordan koden er lagdelt
Koden følger en lille Clean Architecture, så beslutningerne kan testes uden en browser.
| Lag | Indeholder | Kender Selenium? |
| --- | --- | --- |
| `domain` | Taltolkning (`parse_game_number`) og købsvalget (`choose_upgrade`) | Nej |
| `application` | Funktionerne for forelæsningens trin og portene `GamePort` og `Clock` | Nej |
| `infrastructure` | `SeleniumGame` (adapteren) og `SystemClock` | Ja |
| `presentation` | `main` og skærmbilledoptagelsen, kompositionsrødderne | Gennem adapteren |
`constants.py` indeholder adressen, element-id'erne, tiderne og talordene, så en ændring i spillets side er en ændring i én fil.
## Den ene afhængighed
`selenium` er den eneste runtime-afhængighed. En bot kan ikke styre en browser uden, og opgaven bruger det. Begrundelsen og de afviste muligheder står i ADR-0001 (se [Appendiks](Appendix-Documentation-da)).
## Hvad testene beviser
De 99 tests bruger et falsk spil og et falsk ur. De kører på et sekund, kræver hverken Chrome eller netværk og dækker taltolkningen, strategien, løkken og kommandolinjen. Det rigtige spil er kun afprøvet i Docker (se [trin 5](Step-5-Five-Minute-Run-da)).
+28
View File
@@ -0,0 +1,28 @@
# Huraian
🌐 [English](Description) · [Dansk](Description-da) · **Bahasa Melayu**
## Apa itu projek ini
Bot membuka Cookie Clicker dalam Chrome, mengklik kuki besar secepat yang dibenarkan permainan, melawat kedai setiap lima saat dan membeli produk termahal yang mampu dibelinya. Selepas 300 saat ia membaca kuki sesaat dan mencetak `Result : <cps>`.
## Bagaimana kod disusun berlapis
Kod mengikut Clean Architecture yang kecil, supaya keputusan boleh diuji tanpa pelayar.
| Lapisan | Mengandungi | Mengenali Selenium? |
| --- | --- | --- |
| `domain` | Pentafsiran nombor (`parse_game_number`) dan pilihan pembelian (`choose_upgrade`) | Tidak |
| `application` | Fungsi bagi langkah kuliah dan port `GamePort` dan `Clock` | Tidak |
| `infrastructure` | `SeleniumGame` (penyesuai) dan `SystemClock` | Ya |
| `presentation` | `main` dan penangkapan tangkapan skrin, akar komposisi | Melalui penyesuai |
`constants.py` menyimpan alamat, id elemen, masa dan perkataan nombor, jadi perubahan pada halaman permainan hanyalah perubahan dalam satu fail.
## Satu kebergantungan
`selenium` ialah satu-satunya kebergantungan masa jalan. Bot tidak boleh mengawal pelayar tanpanya, dan tugasan menggunakannya. Sebab dan pilihan yang ditolak ada dalam ADR-0001 (lihat [Lampiran](Appendix-Documentation-ms)).
## Apa yang dibuktikan oleh ujian
99 ujian menggunakan permainan palsu dan jam palsu. Ia berjalan dalam satu saat, tidak memerlukan Chrome atau rangkaian, dan merangkumi pentafsir, strategi, gelung dan baris arahan. Permainan sebenar hanya diuji dalam Docker (lihat [langkah 5](Step-5-Five-Minute-Run-ms)).
+28
View File
@@ -0,0 +1,28 @@
# Description
🌐 **English** · [Dansk](Description-da) · [Bahasa Melayu](Description-ms)
## What the project is
The bot opens Cookie Clicker in Chrome, clicks the big cookie as fast as the game allows, visits the store every five seconds and buys the most expensive product it can afford. After 300 seconds it reads the cookies per second and prints `Result : <cps>`.
## How the code is layered
The code follows a small Clean Architecture, so the decisions can be tested without a browser.
| Layer | Holds | Knows about Selenium? |
| --- | --- | --- |
| `domain` | Number parsing (`parse_game_number`) and the purchase choice (`choose_upgrade`) | No |
| `application` | The lecture step functions and the ports `GamePort` and `Clock` | No |
| `infrastructure` | `SeleniumGame` (the adapter) and `SystemClock` | Yes |
| `presentation` | `main` and the screenshot capture, the composition roots | Through the adapter |
`constants.py` holds the address, the element ids, the timings and the number words, so a change in the game's page is a change in one file.
## The one dependency
`selenium` is the only runtime dependency. A bot cannot drive a browser without it, and the assignment uses it. The reasons and the options that were rejected are in ADR-0001 (see [Appendix](Appendix-Documentation)).
## What the tests prove
The 99 tests use a fake game and a fake clock. They run in a second, need no Chrome and no network, and cover the parser, the strategy, the loop and the command line. The real game was exercised only in Docker (see [step 5](Step-5-Five-Minute-Run)).
+20
View File
@@ -0,0 +1,20 @@
# Cookie Clicker Bot
🌐 [English](Home) · **Dansk** · [Bahasa Melayu](Home-ms)
En Selenium-bot, der spiller [Cookie Clicker](https://ozh.github.io/cookieclicker/) i fem minutter: den klikker på den store cookie, køber opgraderinger og melder, hvor mange cookies i sekundet (CPS) den nåede. Det er projektet fra dag 48 i Udemys *100 Days of Code: The Complete Python Pro Bootcamp*.
Denne wiki gennemgår de fem trin fra forelæsningen. Hver side har et skærmbillede, som botten selv har taget i en headless browser i Docker, funktionen der udfører trinnet, og hvad trinnet lærte os.
| Side | Læs den, når du vil |
| --- | --- |
| [Beskrivelse](Description-da) | Se hvad projektet er, og hvordan koden er lagdelt |
| [Installation](Install-da) | Oprette et `.venv`, installere og køre botten og testene |
| [Trin 1: åbn spillet](Step-1-Open-The-Game-da) | Se hvordan botten starter spillet og vælger sprog |
| [Trin 2: klik på cookien](Step-2-Click-The-Cookie-da) | Se hvorfor botten skal bruge rigtige klik |
| [Trin 3: læs antallet](Step-3-Read-The-Count-da) | Se hvordan spillets tal bliver læst |
| [Trin 4: køb opgraderinger](Step-4-Buy-Upgrades-da) | Se hvordan botten vælger og køber produkter |
| [Trin 5: kørslen på fem minutter](Step-5-Five-Minute-Run-da) | Se løkken, resultatet og Docker-kørslen |
| [Appendiks](Appendix-Documentation-da) | Finde projektets dokumenter |
Resultatet af kørslen på skærmbillederne: **16,6 cookies i sekundet** efter 300 sekunder.
+20
View File
@@ -0,0 +1,20 @@
# Bot Cookie Clicker
🌐 [English](Home) · [Dansk](Home-da) · **Bahasa Melayu**
Bot Selenium yang bermain [Cookie Clicker](https://ozh.github.io/cookieclicker/) selama lima minit: ia mengklik kuki besar, membeli peningkatan dan melaporkan kuki sesaat (CPS) yang dicapai. Ini projek hari ke-48 dalam *100 Days of Code: The Complete Python Pro Bootcamp* di Udemy.
Wiki ini menerangkan lima langkah kuliah. Setiap halaman mempunyai tangkapan skrin yang diambil oleh bot sendiri dalam pelayar tanpa kepala (headless) di dalam Docker, fungsi yang melaksanakan langkah itu, dan apa yang dipelajari daripada langkah tersebut.
| Halaman | Baca apabila anda mahu |
| --- | --- |
| [Huraian](Description-ms) | Melihat apa itu projek ini dan bagaimana kod disusun berlapis |
| [Pemasangan](Install-ms) | Mencipta `.venv`, memasang, menjalankan bot dan ujian |
| [Langkah 1: buka permainan](Step-1-Open-The-Game-ms) | Melihat bagaimana bot memulakan permainan dan memilih bahasa |
| [Langkah 2: klik kuki](Step-2-Click-The-Cookie-ms) | Melihat sebab bot memerlukan klik sebenar |
| [Langkah 3: baca bilangan](Step-3-Read-The-Count-ms) | Melihat bagaimana nombor permainan dibaca |
| [Langkah 4: beli peningkatan](Step-4-Buy-Upgrades-ms) | Melihat bagaimana bot memilih dan membeli produk |
| [Langkah 5: larian lima minit](Step-5-Five-Minute-Run-ms) | Melihat gelung, hasil dan larian Docker |
| [Lampiran](Appendix-Documentation-ms) | Mencari dokumen projek |
Hasil larian dalam tangkapan skrin: **16.6 kuki sesaat** selepas 300 saat.
+20
View File
@@ -0,0 +1,20 @@
# Cookie Clicker Bot
🌐 **English** · [Dansk](Home-da) · [Bahasa Melayu](Home-ms)
A Selenium bot that plays [Cookie Clicker](https://ozh.github.io/cookieclicker/) for five minutes: it clicks the big cookie, buys upgrades and reports its cookies per second (CPS). It is the day 48 project of Udemy's *100 Days of Code: The Complete Python Pro Bootcamp*.
This wiki walks through the five lecture steps. Each page has a screenshot taken by the bot itself in a headless browser inside Docker, the function that does the step, and what the step taught.
| Page | Read it when you want to |
| --- | --- |
| [Description](Description) | See what the project is and how the code is layered |
| [Install](Install) | Create a `.venv`, install, run the bot and the tests |
| [Step 1: open the game](Step-1-Open-The-Game) | See how the bot starts the game and chooses a language |
| [Step 2: click the cookie](Step-2-Click-The-Cookie) | See why the bot needs real clicks |
| [Step 3: read the count](Step-3-Read-The-Count) | See how the game's numbers are parsed |
| [Step 4: buy upgrades](Step-4-Buy-Upgrades) | See how the bot chooses and buys products |
| [Step 5: the five-minute run](Step-5-Five-Minute-Run) | See the loop, the result and the Docker run |
| [Appendix](Appendix-Documentation) | Find the project documents |
The result of the run shown in the screenshots: **16.6 cookies per second** after 300 seconds.
+44
View File
@@ -0,0 +1,44 @@
# Installation
🌐 [English](Install) · **Dansk** · [Bahasa Melayu](Install-ms)
## Du skal bruge
- Python 3.13 eller nyere.
- Google Chrome (Selenium Manager henter driveren). Ikke nødvendig til testene.
- Docker, kun for at gentage den headless kørsel og skærmbillederne.
## Opret og brug et lokalt `.venv`
Første blok er til Windows PowerShell, anden til Linux og macOS. Forlad miljøet med `deactivate`.
```powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
```
```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
```
## Kør botten og testene
```bash
python -m cookie_clicker_bot
python -m cookie_clicker_bot --headless --duration 60
python -m pytest
```
## Gentag skærmbillederne i Docker
Kørslen er headless, så den tager aldrig din mus eller dit tastatur. Skærmbillederne og `steps.json` havner i `runs/`.
```bash
docker build -t cookie-clicker-bot -f docker/Dockerfile .
docker run --rm -v "$(pwd)/runs:/runs" cookie-clicker-bot --out /runs
```
+44
View File
@@ -0,0 +1,44 @@
# Pemasangan
🌐 [English](Install) · [Dansk](Install-da) · **Bahasa Melayu**
## Anda memerlukan
- Python 3.13 atau lebih baharu.
- Google Chrome (Selenium Manager memuat turun pemandu). Tidak diperlukan untuk ujian.
- Docker, hanya untuk mengulang larian tanpa kepala dan tangkapan skrin.
## Cipta dan guna `.venv` setempat
Blok pertama untuk Windows PowerShell, yang kedua untuk Linux dan macOS. Keluar daripada persekitaran dengan `deactivate`.
```powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
```
```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
```
## Jalankan bot dan ujian
```bash
python -m cookie_clicker_bot
python -m cookie_clicker_bot --headless --duration 60
python -m pytest
```
## Ulang tangkapan skrin dalam Docker
Larian ini tanpa kepala, jadi ia tidak mengambil tetikus atau papan kekunci anda. Tangkapan skrin dan `steps.json` disimpan dalam `runs/`.
```bash
docker build -t cookie-clicker-bot -f docker/Dockerfile .
docker run --rm -v "$(pwd)/runs:/runs" cookie-clicker-bot --out /runs
```
+44
View File
@@ -0,0 +1,44 @@
# Install
🌐 **English** · [Dansk](Install-da) · [Bahasa Melayu](Install-ms)
## You need
- Python 3.13 or newer.
- Google Chrome (Selenium Manager downloads the driver). Not needed for the tests.
- Docker, only to repeat the headless run and the screenshots.
## Create and use a local `.venv`
The first block is for Windows PowerShell, the second for Linux and macOS. Leave the environment with `deactivate`.
```powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
```
```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
```
## Run the bot and the tests
```bash
python -m cookie_clicker_bot
python -m cookie_clicker_bot --headless --duration 60
python -m pytest
```
## Repeat the screenshots in Docker
The run is headless, so it never takes your mouse or keyboard. The screenshots and `steps.json` land in `runs/`.
```bash
docker build -t cookie-clicker-bot -f docker/Dockerfile .
docker run --rm -v "$(pwd)/runs:/runs" cookie-clicker-bot --out /runs
```
+31
View File
@@ -0,0 +1,31 @@
# Trin 1: åbn spillet
🌐 [English](Step-1-Open-The-Game) · **Dansk** · [Bahasa Melayu](Step-1-Open-The-Game-ms)
Botten starter Chrome, indlæser spillet og venter, til sprogvalget er på siden. Derefter vælger den engelsk, så den store cookie kan bruges.
**Skærmbillede**
![Trin 1: åbn spillet](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-1-open-game.png)
**Funktion**
```python
def open_game(game: GamePort) -> None:
game.open_page(GAME_URL)
game.wait_for_element(LANGUAGE_BUTTON_PREFIX + LANGUAGE_CODE)
def select_language(game: GamePort) -> None:
game.click_element(LANGUAGE_BUTTON_PREFIX + LANGUAGE_CODE)
game.wait_for_element(BIG_COOKIE_ID)
```
## Hvad vi fik ud af det
- **Vent på et element, sov ikke.** `WebDriverWait` vender tilbage, så snart elementet findes, så botten er hurtig, når siden er hurtig, og tålmodig, når den er langsom.
- **Selenium Manager** finder den rigtige Chrome-driver, så der ikke skal hentes noget manuelt.
- **En port holder Selenium ude af reglerne.** `open_game` taler kun med `GamePort`, så et falsk spil kan teste den uden browser.
- **Ét sted til sidens detaljer.** Adressen og element-id'erne står i `constants.py`.
Næste: [Trin 2: klik på cookien](Step-2-Click-The-Cookie-da)
+31
View File
@@ -0,0 +1,31 @@
# Langkah 1: buka permainan
🌐 [English](Step-1-Open-The-Game) · [Dansk](Step-1-Open-The-Game-da) · **Bahasa Melayu**
Bot memulakan Chrome, memuatkan permainan dan menunggu sehingga pilihan bahasa muncul pada halaman. Kemudian ia memilih bahasa Inggeris, supaya kuki besar boleh dimainkan.
**Tangkapan skrin**
![Langkah 1: buka permainan](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-1-open-game.png)
**Fungsi**
```python
def open_game(game: GamePort) -> None:
game.open_page(GAME_URL)
game.wait_for_element(LANGUAGE_BUTTON_PREFIX + LANGUAGE_CODE)
def select_language(game: GamePort) -> None:
game.click_element(LANGUAGE_BUTTON_PREFIX + LANGUAGE_CODE)
game.wait_for_element(BIG_COOKIE_ID)
```
## Apa yang kami peroleh
- **Tunggu elemen, jangan tidur.** `WebDriverWait` kembali sebaik sahaja elemen wujud, jadi bot pantas apabila halaman pantas dan sabar apabila ia perlahan.
- **Selenium Manager** mencari pemandu Chrome yang sepadan, jadi tiada muat turun manual.
- **Port mengasingkan Selenium daripada peraturan.** `open_game` hanya bercakap dengan `GamePort`, jadi permainan palsu boleh mengujinya tanpa pelayar.
- **Satu tempat untuk butiran halaman.** Alamat dan id elemen berada dalam `constants.py`.
Seterusnya: [Langkah 2: klik kuki](Step-2-Click-The-Cookie-ms)
+31
View File
@@ -0,0 +1,31 @@
# Step 1: open the game
🌐 **English** · [Dansk](Step-1-Open-The-Game-da) · [Bahasa Melayu](Step-1-Open-The-Game-ms)
The bot starts Chrome, loads the game and waits until the language prompt is on the page. Then it picks English, which makes the big cookie playable.
**Screenshot**
![Step 1: open the game](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-1-open-game.png)
**Function**
```python
def open_game(game: GamePort) -> None:
game.open_page(GAME_URL)
game.wait_for_element(LANGUAGE_BUTTON_PREFIX + LANGUAGE_CODE)
def select_language(game: GamePort) -> None:
game.click_element(LANGUAGE_BUTTON_PREFIX + LANGUAGE_CODE)
game.wait_for_element(BIG_COOKIE_ID)
```
## What we gained
- **Wait for an element, do not sleep.** `WebDriverWait` returns as soon as the element exists, so the bot is fast when the page is fast and patient when it is slow.
- **Selenium Manager** finds the matching Chrome driver, so there is no manual download.
- **A port keeps Selenium out of the rules.** `open_game` only talks to `GamePort`, so a fake game tests it without a browser.
- **One place for the page's details.** The address and the element ids are in `constants.py`.
Next: [Step 2: click the cookie](Step-2-Click-The-Cookie)
+27
View File
@@ -0,0 +1,27 @@
# Trin 2: klik på cookien
🌐 [English](Step-2-Click-The-Cookie) · **Dansk** · [Bahasa Melayu](Step-2-Click-The-Cookie-ms)
`click_cookie` klikker et antal gange på den store cookie. Efter 15 klik viser skærmbilledet tælleren og de første cookies i bageriet.
**Skærmbillede**
![Trin 2: klik på cookien](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-2-click-cookie.png)
**Funktion**
```python
def click_cookie(game: GamePort, times: int = 1) -> None:
if times < 0:
raise ValueError(f"times must not be negative, got {times}")
for _ in range(times):
game.click_element(BIG_COOKIE_ID)
```
## Hvad vi fik ud af det
- **Et rigtigt klik, ikke et script-klik.** Spillet ignorerer syntetiske klik over cirka 3 i sekundet, men tager imod cirka 50 i sekundet fra et rigtigt klik, så `SeleniumGame` bruger altid `element.click()`.
- **Pas på inddata.** Et negativt antal klik giver `ValueError`, og nul klik er tilladt; begge dele er testet.
- **Prøv igen, når siden ændrer sig.** Spillet tegner siden om ofte, så et klik på et forældet element prøves igen et par gange.
Næste: [Trin 3: læs antallet](Step-3-Read-The-Count-da)
+27
View File
@@ -0,0 +1,27 @@
# Langkah 2: klik kuki
🌐 [English](Step-2-Click-The-Cookie) · [Dansk](Step-2-Click-The-Cookie-da) · **Bahasa Melayu**
`click_cookie` mengklik kuki besar beberapa kali. Selepas 15 klik, tangkapan skrin menunjukkan pembilang dan kuki pertama di kedai roti.
**Tangkapan skrin**
![Langkah 2: klik kuki](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-2-click-cookie.png)
**Fungsi**
```python
def click_cookie(game: GamePort, times: int = 1) -> None:
if times < 0:
raise ValueError(f"times must not be negative, got {times}")
for _ in range(times):
game.click_element(BIG_COOKIE_ID)
```
## Apa yang kami peroleh
- **Klik sebenar, bukan klik skrip.** Permainan mengabaikan klik sintetik melebihi kira-kira 3 sesaat tetapi menerima kira-kira 50 sesaat daripada klik sebenar, jadi `SeleniumGame` sentiasa menggunakan `element.click()`.
- **Kawal input.** Bilangan klik negatif menimbulkan `ValueError`, dan sifar klik dibenarkan; kedua-duanya diuji.
- **Cuba semula apabila halaman berubah.** Permainan melukis semula halamannya dengan kerap, jadi klik pada elemen yang usang dicuba semula beberapa kali.
Seterusnya: [Langkah 3: baca bilangan](Step-3-Read-The-Count-ms)
+27
View File
@@ -0,0 +1,27 @@
# Step 2: click the cookie
🌐 **English** · [Dansk](Step-2-Click-The-Cookie-da) · [Bahasa Melayu](Step-2-Click-The-Cookie-ms)
`click_cookie` clicks the big cookie a number of times. After 15 clicks the screenshot shows the counter and the first cookies in the bakery.
**Screenshot**
![Step 2: click the cookie](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-2-click-cookie.png)
**Function**
```python
def click_cookie(game: GamePort, times: int = 1) -> None:
if times < 0:
raise ValueError(f"times must not be negative, got {times}")
for _ in range(times):
game.click_element(BIG_COOKIE_ID)
```
## What we gained
- **A real click, not a script click.** The game ignores synthetic clicks above about 3 per second but accepts about 50 per second from a real click, so `SeleniumGame` always uses `element.click()`.
- **Guard the input.** A negative number of clicks raises `ValueError`, and zero clicks is allowed; both are tested.
- **Retry when the page changes.** The game redraws its page often, so a click that hits a stale element is tried again a few times.
Next: [Step 3: read the count](Step-3-Read-The-Count)
+26
View File
@@ -0,0 +1,26 @@
# Trin 3: læs antallet
🌐 [English](Step-3-Read-The-Count) · **Dansk** · [Bahasa Melayu](Step-3-Read-The-Count-ms)
`get_cookie_count` læser tællerens første linje og gør den til et heltal. Spillet skriver `12,345` og, ved store beløb, `1.234 million`.
**Skærmbillede**
![Trin 3: læs antallet](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-3-read-count.png)
**Funktion**
```python
def get_cookie_count(game: GamePort) -> int:
first_line = game.read_text(COOKIE_COUNTER_ID).strip().splitlines()[0]
return parse_game_number(first_line)
```
## Hvad vi fik ud af det
- **Læs spillets eget talformat.** Kommaer grupperer tusinder, og talord (`million`, `billion`, op til `decillion`) skalerer værdien.
- **Brug `Decimal`, ikke `float`.** En første version brugte `float`, og `3 decillion` blev forkert; `Decimal` er præcis. En fejlende test fandt det.
- **Fejl med en tydelig fejl.** Tekst uden et indledende tal giver `UnreadableNumberError` i stedet for en forkert værdi.
- **Test grænsetilfældene.** Tom tekst, ental og flertal (`1 cookie`), ukendte ord og afrunding er alle med i testene.
Næste: [Trin 4: køb opgraderinger](Step-4-Buy-Upgrades-da)
+26
View File
@@ -0,0 +1,26 @@
# Langkah 3: baca bilangan
🌐 [English](Step-3-Read-The-Count) · [Dansk](Step-3-Read-The-Count-da) · **Bahasa Melayu**
`get_cookie_count` membaca baris pertama pembilang dan menukarkannya kepada nombor bulat. Permainan menulis `12,345` dan, bagi jumlah besar, `1.234 million`.
**Tangkapan skrin**
![Langkah 3: baca bilangan](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-3-read-count.png)
**Fungsi**
```python
def get_cookie_count(game: GamePort) -> int:
first_line = game.read_text(COOKIE_COUNTER_ID).strip().splitlines()[0]
return parse_game_number(first_line)
```
## Apa yang kami peroleh
- **Tafsir format nombor permainan sendiri.** Koma mengumpulkan ribuan dan perkataan nombor (`million`, `billion`, hingga `decillion`) menskalakan nilai.
- **Guna `Decimal`, bukan `float`.** Versi pertama menggunakan `float` dan `3 decillion` menjadi salah; `Decimal` tepat. Ujian yang gagal menemuinya.
- **Gagal dengan ralat yang jelas.** Teks tanpa nombor di hadapan menimbulkan `UnreadableNumberError` dan bukannya nilai yang salah.
- **Uji kes tepi.** Teks kosong, tunggal dan jamak (`1 cookie`), perkataan tidak dikenali dan pembundaran semuanya ada dalam ujian.
Seterusnya: [Langkah 4: beli peningkatan](Step-4-Buy-Upgrades-ms)
+26
View File
@@ -0,0 +1,26 @@
# Step 3: read the count
🌐 **English** · [Dansk](Step-3-Read-The-Count-da) · [Bahasa Melayu](Step-3-Read-The-Count-ms)
`get_cookie_count` reads the first line of the counter and turns it into a whole number. The game writes `12,345` and, for big amounts, `1.234 million`.
**Screenshot**
![Step 3: read the count](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-3-read-count.png)
**Function**
```python
def get_cookie_count(game: GamePort) -> int:
first_line = game.read_text(COOKIE_COUNTER_ID).strip().splitlines()[0]
return parse_game_number(first_line)
```
## What we gained
- **Parse the game's own number format.** Commas group thousands and number words (`million`, `billion`, up to `decillion`) scale the value.
- **Use `Decimal`, not `float`.** A first version used `float` and `3 decillion` came out wrong; `Decimal` is exact. A failing test found it.
- **Fail with a clear error.** Text without a leading number raises `UnreadableNumberError` instead of returning a wrong value.
- **Test the edge cases.** Empty text, a plural and a singular (`1 cookie`), unknown words and rounding are all in the tests.
Next: [Step 4: buy upgrades](Step-4-Buy-Upgrades)
+28
View File
@@ -0,0 +1,28 @@
# Trin 4: køb opgraderinger
🌐 [English](Step-4-Buy-Upgrades) · **Dansk** · [Bahasa Melayu](Step-4-Buy-Upgrades-ms)
`get_store_items` læser butikkens 20 produkter, `choose_upgrade` vælger det dyreste, botten har råd til, og `buy_upgrade` klikker på det. Skærmbilledet viser den første markør, der er købt.
**Skærmbillede**
![Trin 4: køb opgraderinger](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-4-buy-upgrade.png)
**Funktion**
```python
def choose_upgrade(items: Sequence[StoreItem]) -> StoreItem | None:
affordable = [item for item in items if item.is_affordable]
if not affordable:
return None
return max(affordable, key=lambda item: (item.price, item.index))
```
## Hvad vi fik ud af det
- **Hold beslutningen ren.** `choose_upgrade` tager almindelige `StoreItem`-værdier og returnerer én, så den testes uden noget spil (lige priser, intet overkommeligt, tom liste).
- **Bekræft resultatet, antag det ikke.** `buy_upgrade` tjekker, at det ejede antal (`productOwned<N>`) steg. At tjekke antallet af cookies først var forkert: antallet stiger, mens botten klikker.
- **Siden halter bagefter.** Den rigtige kørsel viste, at cirka 40 procent af købsforsøgene mislykkes, fordi butikkens markeringer læses lige før spillet tegner om. Botten logger det og prøver igen ved næste butiksbesøg.
- **Det rigtige spil finder det, falske ikke kan.** Genforsøget ved forældede elementer og tjekket af ejet antal kom begge fra kørsler i Docker.
Næste: [Trin 5: kørslen på fem minutter](Step-5-Five-Minute-Run-da)
+28
View File
@@ -0,0 +1,28 @@
# Langkah 4: beli peningkatan
🌐 [English](Step-4-Buy-Upgrades) · [Dansk](Step-4-Buy-Upgrades-da) · **Bahasa Melayu**
`get_store_items` membaca 20 produk kedai, `choose_upgrade` memilih yang termahal yang mampu dibeli bot, dan `buy_upgrade` mengkliknya. Tangkapan skrin menunjukkan kursor pertama yang dibeli.
**Tangkapan skrin**
![Langkah 4: beli peningkatan](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-4-buy-upgrade.png)
**Fungsi**
```python
def choose_upgrade(items: Sequence[StoreItem]) -> StoreItem | None:
affordable = [item for item in items if item.is_affordable]
if not affordable:
return None
return max(affordable, key=lambda item: (item.price, item.index))
```
## Apa yang kami peroleh
- **Pastikan keputusan bersih.** `choose_upgrade` menerima nilai `StoreItem` biasa dan memulangkan satu, jadi ia diuji tanpa permainan langsung (harga sama, tiada yang mampu dibeli, senarai kosong).
- **Sahkan hasilnya, jangan andaikan.** `buy_upgrade` menyemak bahawa jumlah dimiliki (`productOwned<N>`) bertambah. Menyemak bilangan kuki dahulu adalah salah: bilangan terus meningkat semasa bot mengklik.
- **Halaman ketinggalan.** Larian sebenar menunjukkan kira-kira 40 peratus percubaan pembelian gagal kerana bendera kedai dibaca sebelum permainan melukis semula. Bot mencatatkannya dan mencuba lagi pada lawatan kedai seterusnya.
- **Permainan sebenar menemui apa yang tidak dapat ditemui oleh yang palsu.** Percubaan semula elemen usang dan semakan jumlah dimiliki kedua-duanya datang daripada larian dalam Docker.
Seterusnya: [Langkah 5: larian lima minit](Step-5-Five-Minute-Run-ms)
+28
View File
@@ -0,0 +1,28 @@
# Step 4: buy upgrades
🌐 **English** · [Dansk](Step-4-Buy-Upgrades-da) · [Bahasa Melayu](Step-4-Buy-Upgrades-ms)
`get_store_items` reads the 20 store products, `choose_upgrade` picks the most expensive one the bot can afford, and `buy_upgrade` clicks it. The screenshot shows the first cursor bought.
**Screenshot**
![Step 4: buy upgrades](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-4-buy-upgrade.png)
**Function**
```python
def choose_upgrade(items: Sequence[StoreItem]) -> StoreItem | None:
affordable = [item for item in items if item.is_affordable]
if not affordable:
return None
return max(affordable, key=lambda item: (item.price, item.index))
```
## What we gained
- **Keep the decision pure.** `choose_upgrade` takes plain `StoreItem` values and returns one, so it is tested with no game at all (ties, none affordable, empty list).
- **Confirm the result, do not assume it.** `buy_upgrade` checks that the owned amount (`productOwned<N>`) went up. Checking the cookie count first was wrong: the count keeps rising while the bot clicks.
- **The page lags behind.** The live run showed that about 40 percent of the purchase attempts fail because the store flags are read just before the game redraws. The bot logs it and tries again at the next store check.
- **The real game finds what fakes cannot.** The stale-element retry and the owned-amount check both came from running in Docker.
Next: [Step 5: the five-minute run](Step-5-Five-Minute-Run)
+27
View File
@@ -0,0 +1,27 @@
# Trin 5: kørslen på fem minutter
🌐 [English](Step-5-Five-Minute-Run) · **Dansk** · [Bahasa Melayu](Step-5-Five-Minute-Run-ms)
`run_bot` spiller i 300 sekunder i én løkke: en byge på 20 klik, og hvert 5. sekund et besøg i butikken. Til sidst læser `get_cps` cookies i sekundet. Kørslen på skærmbilledet nåede **16,6 cookies i sekundet** med 26 markører og 14 bedstemødre.
**Skærmbillede**
![Trin 5: kørslen på fem minutter](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-5-result.png)
**Funktion**
```python
while now - start < duration_seconds:
click_cookie(game, CLICKS_PER_BURST)
now = clock.monotonic()
if now >= next_store_check:
_buy_while_affordable(game)
next_store_check = now + STORE_CHECK_INTERVAL_SECONDS
```
## Hvad vi fik ud af det
- **Én løkke, ingen tråde.** Den tidligere løsning brugte to tråde og en ekstra pakke. Én løkke er lettere at læse, kræver ingen ekstra afhængighed og er let at teste.
- **Injicér uret.** `run_bot` får et `Clock`, så et falsk ur tester en kørsel på 300 sekunder på millisekunder.
- **Kør GUI'en i Docker.** Headless Chromium i en container tager aldrig din mus eller dit tastatur, og skærmbillederne og loggen kommer ud som filer.
- **Videre derfra.** Køb efter pris pr. cookie i sekundet, køb opgraderingerne og klik på den gyldne cookie: hver er en ny funktion bag de samme porte.
+27
View File
@@ -0,0 +1,27 @@
# Langkah 5: larian lima minit
🌐 [English](Step-5-Five-Minute-Run) · [Dansk](Step-5-Five-Minute-Run-da) · **Bahasa Melayu**
`run_bot` bermain selama 300 saat dalam satu gelung: satu rentetan 20 klik, kemudian setiap 5 saat satu lawatan ke kedai. Pada akhirnya `get_cps` membaca kuki sesaat. Larian dalam tangkapan skrin mencapai **16.6 kuki sesaat** dengan 26 kursor dan 14 nenek.
**Tangkapan skrin**
![Langkah 5: larian lima minit](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-5-result.png)
**Fungsi**
```python
while now - start < duration_seconds:
click_cookie(game, CLICKS_PER_BURST)
now = clock.monotonic()
if now >= next_store_check:
_buy_while_affordable(game)
next_store_check = now + STORE_CHECK_INTERVAL_SECONDS
```
## Apa yang kami peroleh
- **Satu gelung, tiada thread.** Penyelesaian terdahulu menggunakan dua thread dan satu pakej tambahan. Satu gelung lebih mudah dibaca, tidak memerlukan kebergantungan tambahan dan mudah diuji.
- **Suntik jam.** `run_bot` menerima `Clock`, jadi jam palsu menguji larian 300 saat dalam milisaat.
- **Jalankan GUI dalam Docker.** Chromium tanpa kepala dalam bekas tidak pernah mengambil tetikus atau papan kekunci anda, dan tangkapan skrin serta log keluar sebagai fail.
- **Langkah seterusnya.** Beli mengikut kos setiap kuki sesaat, beli peningkatan dan klik kuki emas: setiap satu ialah fungsi baharu di belakang port yang sama.
+27
View File
@@ -0,0 +1,27 @@
# Step 5: the five-minute run
🌐 **English** · [Dansk](Step-5-Five-Minute-Run-da) · [Bahasa Melayu](Step-5-Five-Minute-Run-ms)
`run_bot` plays for 300 seconds in one loop: a burst of 20 clicks, then every 5 seconds a visit to the store. At the end `get_cps` reads the cookies per second. The run in the screenshot reached **16.6 cookies per second** with 26 cursors and 14 grandmas.
**Screenshot**
![Step 5: the five-minute run](https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/048-the-coockie-factory/raw/branch/main/docs/images/step-5-result.png)
**Function**
```python
while now - start < duration_seconds:
click_cookie(game, CLICKS_PER_BURST)
now = clock.monotonic()
if now >= next_store_check:
_buy_while_affordable(game)
next_store_check = now + STORE_CHECK_INTERVAL_SECONDS
```
## What we gained
- **One loop, no threads.** The earlier solution used two threads and an extra package. One loop is easier to read, needs no extra dependency and is easy to test.
- **Inject the clock.** `run_bot` receives a `Clock`, so a fake clock tests a 300 second run in milliseconds.
- **Run the GUI in Docker.** Headless Chromium in a container never takes your mouse or keyboard, and the screenshots and the log come out as files.
- **Where to go next.** Buy by cost per cookie per second, buy the upgrade crates, and click the golden cookie: each is a new function behind the same ports.
+17
View File
@@ -0,0 +1,17 @@
**Cookie Clicker Bot**
- [Cookie Clicker Bot](Home)
- [Description](Description)
- [Install](Install)
- [Step 1: open the game](Step-1-Open-The-Game)
- [Step 2: click the cookie](Step-2-Click-The-Cookie)
- [Step 3: read the count](Step-3-Read-The-Count)
- [Step 4: buy upgrades](Step-4-Buy-Upgrades)
- [Step 5: the five-minute run](Step-5-Five-Minute-Run)
- [Appendix](Appendix-Documentation)
**Languages**
- English: [Home](Home)
- Dansk: [Hjem](Home-da)
- Bahasa Melayu: [Utama](Home-ms)