11 Commits
Author SHA1 Message Date
david-spacilandClaude Sonnet 5 5dee428752 docs: přidat CLAUDE.md s přehledem architektury a příkazů
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-06-30 22:25:43 +02:00
david-spacil e2d689a618 Merge pull request 'hotfix: odstranění hodnoty pro 2×1 – redundantní' (#29) from hotfix/oprava-hodnoty into main
Reviewed-on: david/kostky#29
2026-06-28 07:49:53 +02:00
David Spáčil c53a58c8e4 hotfix: odstranění hodnoty pro 2×1 – redundantní 2026-06-28 07:48:08 +02:00
david-spacil 48f8998d7c Merge pull request 'release: v0.2.0' (#28) from version/v0.2.0 into main
Reviewed-on: david/kostky#28
2026-06-27 19:42:21 +02:00
David Spáčil 17ccd3b61a fix: změna z čísel na písmena v nabídce možností 2026-06-27 19:41:02 +02:00
david-spacil 1fc55d90f3 Aktualizovat README.md 2026-06-27 19:40:09 +02:00
david-spacil 0a83d27962 Merge pull request 'enhancement: výběr kostek písmeny místo čísel' (#27) from enhancement/pismena-misto-cisel into version/v0.2.0
Reviewed-on: david/kostky#27
2026-06-27 19:28:30 +02:00
David Spáčil 06474e7f68 enhancement/pismena: převod z čísel na písmena a zpět 2026-06-27 19:24:19 +02:00
david-spacil d7648a196c Merge pull request 'build: zabalení hry do instalovatelného Python balíčku' (#26) from package into version/v0.2.0
Reviewed-on: david/kostky#26
2026-06-27 19:18:48 +02:00
David Spáčil db3f71592c fix: oprava anotace to_sorted_tuple() 2026-06-27 18:47:54 +02:00
David Spáčil 69b30552cb fix: opravy (načítání csv + ruff) 2026-06-27 18:38:40 +02:00
4 changed files with 114 additions and 51 deletions

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project
`kostky` is a Czech-language terminal CLI game — a Farkle/"tisícovky" dice game variant for multiple players. Single runtime dependency: `tabulate` (for the final results table).
## Commands
```bash
pip install -e ".[dev]" # editable install with mypy + ruff
kostky # run the game (after install)
python -m kostky # run without installing (requires tabulate)
mypy # type-check (strict mode, see pyproject.toml)
ruff check . # lint
```
There are no tests in this repository.
## Architecture
The entire game lives in `kostky/kostky.py` as a single module with module-level mutable state (no classes):
- `hodnoty: dict[str, int]` — scoring table, loaded at import time from `kostky/hodnoty.csv` via `importlib.resources`. Keys are sorted-digit strings representing dice combos (e.g. `"111"` = three ones), values are point totals.
- `players: dict[str, list[int]]` — per-player history of per-turn scores (insertion order == turn order).
- `aggr: dict[str, int]` — per-player running total, used for win checks and the results table.
- `pointer` / `first` — current player's name / the player who started the round (used to detect when a full round has completed in `next_player`/`check_win`).
- `score` — points accumulated *within the current turn*, reset on bust or on banking (`k`).
- `h_bool` — guards re-rolling: you can't roll again until you've banked at least one scoring combination in the current roll sequence.
Control flow is a manual state machine driven by tuples, not exceptions or return codes:
- `evaluate(inp, count, throw)` parses a single line of user input against the current dice state and returns a `data` tuple whose first element is a tag: `"count"` (continue turn with N remaining dice and a throw string), `"msg"` (show an error/message, possibly re-prompting), `"win"`, or `"exit"`.
- `game(data)` is the main loop: it pattern-matches on `data[0]`, prints state, prompts for input, and calls `evaluate` again to get the next `data` tuple. This loop is how every turn — and the whole game — progresses.
- Dice picks are entered as letters (`a`-`f` mapped to position 1-6 via `ord(x)-96`), not digit values; `evaluate` converts letters to positions internally before checking the picked dice values against `hodnoty`.
- Bust detection (`check_value`) checks whether *any* key in `hodnoty` is a substring of the sorted current throw — not a full combinatorial scoring check, so this is the single source of truth for "is this throw dead."
- Win condition (`check_win`) only fires once `pointer` cycles back to `first`, i.e. checked once per full round rather than once per turn, and only declares a winner if exactly one player is alone at/above `final_score`.
`kostky/__main__.py` and the `kostky` console-script entry point (in `pyproject.toml`) both call `kostky.kostky.main()`.
## Scoring data
`kostky/hodnoty.csv` (digit-string-of-dice-values → points) is the single source of truth for scoring and is also documented in tables in `README.md`. If you change one, update the other.
+28 -22
View File
@@ -1,25 +1,28 @@
# kostky
Textová (CLI) hra v kostky pro více hráčů – varianta klasického *Farkle*
(„tisícovky“). Běží celá v terminálu a je napsaná v čistém Pythonu bez
externích závislostí.
(„tisícovky“). Běží celá v terminálu, v Pythonu, s jedinou závislostí
(`tabulate` pro závěrečnou výsledkovou tabulku).
## Požadavky
- Python 3.7+ (hra se spoléhá na zachování pořadí klíčů ve slovníku)
- Žádné externí balíčky – používá se jen standardní knihovna
(`random`, `os`, `re`, `csv`, `collections`, `typing`)
- Python 3.9+
- Závislost `tabulate` (doinstaluje se automaticky při instalaci)
## Spuštění
## Instalace a spuštění
```bash
git clone https://gitea.spacilovi.eu/david/kostky.git
cd kostky
python main.py
pip install .
kostky
```
> Hru spouštěj z adresáře projektu – soubor `hodnoty.csv` (bodovací tabulka)
> se načítá relativní cestou vůči aktuálnímu adresáři.
Případně bez instalace (s nainstalovaným `tabulate`) přes `python -m kostky`,
nebo izolovaně jako CLI nástroj přes `pipx install .`.
> Pro vývoj: `pip install -e ".[dev]"` – editovatelná instalace včetně `mypy`
> a `ruff`.
## Jak se hraje
@@ -31,42 +34,45 @@ python main.py
### Průběh tahu
Na začátku tahu uvidíš cílové skóre, kdo je na tahu (a jeho celkové skóre)
a hod šesti kostkami s očíslovanými pozicemi:
Na začátku tahu uvidíš, kdo je na tahu (s jeho skóre a cílem), a hod šesti
kostkami s písmennými pozicemi:
```
Vítězné skóre: 10000.
Na tahu je Anna (0).
Na tahu je Anna (0/10000).
1, 5, 3, 2, 6, 4
^ ^ ^ ^ ^ ^
1 2 3 4 5 6
a b c d e f
```
Pak zvolíš jednu z akcí:
| Vstup | Akce |
| --- | --- |
| `pozice kostek` | Odložíš vybrané kostky k vyhodnocení (např. `1`, `34`, `123456`). Hodnoty na zvolených pozicích musí dohromady tvořit některou kombinaci z bodovací tabulky. |
| `pozice kostek` | Odložíš vybrané kostky k vyhodnocení – písmeny (např. `a`, `cd`, `abcdef`). Hodnoty na zvolených pozicích musí dohromady tvořit některou kombinaci z bodovací tabulky. |
| `h` | Hodíš znovu zbývajícími kostkami. Povoleno až poté, co v tomto tahu odložíš aspoň jednu bodující kombinaci. |
| `k` | Ukončíš tah, připíšeš si nasbírané skóre a předáš tah dalšímu hráči. |
| `exit` | Předčasně ukončíš celou hru. |
Odkládat můžeš i víc kombinací po sobě – po každém vyhodnocení pokračuješ se
zbývajícími kostkami a body se v rámci tahu sčítají.
### Smůla, hot dice a výhra
### Smůla, hot dice, vynulování a výhra
- **Smůla (bust):** když čerstvý hod neobsahuje žádnou bodující kombinaci,
o body z tohoto tahu přijdeš a tah přechází na dalšího hráče.
- **Hot dice:** když se ti podaří odložit všech šest kostek, dostaneš čerstvý
hod šesti kostkami a pokračuješ ve stejném tahu.
- **Výhra:** po dokončení každého kola se kontroluje, jestli někdo dosáhl
cílového skóre. Vyhrává osamocený lídr nad limitem; při shodě na první
příčce se hraje dál.
- **Vynulování:** tři po sobě jdoucí nulové tahy vynulují tvé celkové skóre.
Počet aktuálních nul se vedle skóre ukazuje jako `x` / `xx`.
- **Výhra:** po dokončení každého kola vyhrává osamocený lídr nad cílovým
skóre; při shodě na první příčce se hraje dál. Na konci se vypíše výsledková
tabulka s pořadím všech hráčů.
## Bodování
Kompletní tabulka je v souboru [`hodnoty.csv`](hodnoty.csv). Kombinace se
vyhodnocuje podle hodnot odložených kostek – na jejich pořadí nezáleží.
Kompletní tabulka je v souboru [`kostky/hodnoty.csv`](kostky/hodnoty.csv).
Kombinace se vyhodnocuje podle hodnot odložených kostek – na jejich pořadí
nezáleží.
### Stejné kostky
@@ -76,7 +82,7 @@ vyhodnocuje podle hodnot odložených kostek – na jejich pořadí nezáleží.
| **2** | – | – | 200 | 400 | 800 | 1600 |
| **3** | – | – | 300 | 600 | 1200 | 2400 |
| **4** | – | – | 400 | 800 | 1600 | 3200 |
| **5** | 50 | – | 500 | 1000 | 2000 | 4000 |
| **5** | 50 | 100 | 500 | 1000 | 2000 | 4000 |
| **6** | – | – | 600 | 1200 | 2400 | 4800 |
(„–“ = daná kombinace samostatně neboduje.)
-1
View File
@@ -24,7 +24,6 @@
444,400
2222,400
333,300
11,200
222,200
1,100
5,50
1 111111 8000
24 444 400
25 2222 400
26 333 300
11 200
27 222 200
28 1 100
29 5 50
+42 -28
View File
@@ -1,10 +1,11 @@
from typing import Any
from random import randint
from os import system, name
import re
from importlib.resources import files
import csv
import re
from collections import Counter
from importlib.resources import files
from os import name, system
from random import randint
from typing import Any
from tabulate import tabulate
score: int = 0
@@ -16,11 +17,10 @@ h_bool: bool = True
aggr: dict[str, int] = {}
final_score: int = 10000
with open("hodnoty.csv", "r") as csv_file:
csv_text = files(__package__).joinpath("hodnoty.csv").read_text(encoding="utf-8")
csv_reader = csv.reader(csv_text.splitlines())
csv_text = files(__package__).joinpath("hodnoty.csv").read_text(encoding="utf-8")
csv_reader = csv.reader(csv_text.splitlines())
for row in csv_reader:
for row in csv_reader:
key = str(row[0])
value = int(row[1])
@@ -29,7 +29,7 @@ with open("hodnoty.csv", "r") as csv_file:
def throw(count: int) -> str:
count = int(count)
new_throw: list[str] = []
for dice in range(count):
for _dice in range(count):
new_throw.append(str(randint(1, 6)))
new_throw_str: str = "".join(new_throw)
return new_throw_str
@@ -41,18 +41,15 @@ def show_throw(throw: str) -> str:
pretty_list: str = ", ".join(throw_temp)
pretty_list += "\n"
for i in range(1, len(throw_temp) + 1):
for _i in range(1, len(throw_temp) + 1):
pretty_list += "^ "
pretty_list += "\n"
for i in range(1, len(throw_temp) + 1):
pretty_list += f"{i} "
for i in range(len(throw_temp)):
pretty_list += f"{chr(97+i)} "
return pretty_list
def clear() -> None:
if name == 'nt':
_ = system('cls')
else:
_ = system('clear')
_ = system('cls') if name == 'nt' else system('clear')
def evaluate(inp: Any, count: int, throw: str) -> tuple[Any, ...]:
@@ -78,19 +75,34 @@ def evaluate(inp: Any, count: int, throw: str) -> tuple[Any, ...]:
pointer = nxt_key
inp = str(inp)
if re.search(r"^\d+$", inp):
inp_temp: str = ""
if re.search(r"^[a-f]+$", inp):
if len(inp) > count:
data: tuple[Any, ...] = ("msg", "ER01", "Tolika kostkami nemůžete hodit. Zkuste to znovu: ", count, throw)
data: tuple[Any, ...] = (
"msg",
"ER01",
"Tolika kostkami nemůžete hodit. Zkuste to znovu: ",
count,
throw
)
else:
for x in inp:
x = int(x)
x = ord(x)-96
if x > count:
if count == 1:
ran = "1"
else:
ran = f"1–{count}"
data = ("msg", "ER02", f"Zvolte prosím kostky v platném rozsahu ({ran}): ", count, throw)
ran = "1" if count == 1 else f"1–{count}"
data = (
"msg",
"ER02",
f"Zvolte prosím kostky v platném rozsahu ({ran}): ",
count,
throw
)
return data
else:
inp_temp += str(x)
inp = inp_temp
old_throw: list[str] = []
pick: list[str] = []
for y in range(count):
@@ -205,8 +217,9 @@ def add_players() -> None:
else:
break
def to_sorted_tuple(d: dict[str, int], s: bool = True) -> tuple[list[tuple[int, str, int]], dict[str, int]]:
L: list[tuple[str, int]] = []
def to_sorted_tuple(d: dict[str, int], s: bool = True) -> tuple[list[tuple[int, str, int]],
dict[str, int]]:
L: list[tuple[int, str, int]] = []
if s:
d = dict(sorted(d.items(), key=lambda x: x[1], reverse=True))
@@ -278,7 +291,8 @@ def game(data: tuple) -> None:
if count == 0:
count = 6
msg: str = ("Jakou akci chcete provést?\n"
" - Zadejte kombinaci kostek k ponechání (dojde k jejich vyhodnocení): [pozice kostek] (např. 1 nebo 34 nebo 123456).\n"
" - Zadejte kombinaci kostek k ponechání (dojde k jejich vyhodnocení): "
"[pozice kostek] (např. a nebo cd nebo abcdef).\n"
" - Hodit znovu: [h].\n"
f" - Ukončit hod a ponechat si skóre z tohoto hodu ({score}): [k].\n"
" - Ukončit hru: [exit].\n"