49 Commits

Author SHA1 Message Date
david-spacil 8c8e029a70 Merge pull request 'Automatické vyhodnocení a nová dynamika kola' (#31) from feature/automaticke-vyhodnoceni into version/v0.3.0
Reviewed-on: #31
2026-07-12 20:53:28 +02:00
david-spacil c2bd4fd6ac chore: nastavit mypy python_version na 3.10 2026-07-12 20:45:53 +02:00
david-spacil 222df46c47 docs: přidat pravidlo o nepředkládání kódu do CLAUDE.md 2026-07-12 20:45:53 +02:00
david-spacil 21efddc1a0 style: drobný úklid pro ruff a mypy
key bez zbytečného str(), odstranění count = int(count) v throw().
2026-07-12 20:45:31 +02:00
david-spacil b3650b7243 refactor: clear() přes subprocess místo os.system 2026-07-12 20:45:31 +02:00
david-spacil b070e22f33 feat: automatické vyhodnocení a nová dynamika kola
Jedno vyhodnocení kostek = jeden hod; poté nový hod (h), ukončení tahu (k), nebo exit (x). Přepis game() a evaluate() na použití eval_wrapper, odstranění globálů score a h_bool. Zahrnuje správné chování smůly (ztráta bodů z tahu), potvrzení výběru a opravu zobrazení strikes.
2026-07-12 20:45:31 +02:00
david-spacil e9cf875192 refactor: vytažení funkcí z evaluate
Funkce eval_score vytažena z evaluate; translate_letters_to_numbers,
pick_throw_proc a eval_wrapper nově vytvořeny z fragmentů evaluate.
Oproti dřívějšímu pokusu odstraněny i přesunuté (redundantní) řádky.
2026-07-12 20:23:26 +02:00
david-spacil caa743fcc9 update .gitignore 2026-07-11 22:46:25 +02:00
david-spacil 26f53761a7 enhancement: řazení hodnot
po načtení hodnot kombinací z csv dojde pro jistotu ještě k seřazení podle hodnoty
2026-07-11 16:30:33 +02:00
david-spacil d420664fde feat: upravena funkce eval_score()
Došlo k úpravě funkce eval_score() tak, aby přijímala input s více kombinacemi najednou. V případě vstupu, který kromě platných kombinací obsahuje i hodnoty navíc, vrátí False a uživatel má možnost vybrat kostky k vyhodnocení znovu
2026-07-11 16:16:39 +02:00
david-spacil 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
David Spáčil a9ef19ee52 package: změna názvu importu v __main__.py 2026-06-27 18:18:37 +02:00
David Spáčil a31eff26fd package: přidat pyproject.toml 2026-06-27 18:18:16 +02:00
David Spáčil 9346735af6 package: přejmenovat main.py na kostky.py 2026-06-27 18:16:04 +02:00
David Spáčil 3808081482 package: povinná struktura 2026-06-27 18:10:02 +02:00
David Spáčil 9d5c51a17a package: bezpečné otevírání csv 2026-06-27 18:09:43 +02:00
David Spáčil 388c92f817 package: přesuny souborů 2026-06-27 18:09:15 +02:00
David Spáčil 754119980f package: update .gitignore 2026-06-27 18:05:57 +02:00
david-spacil 74849ff503 Merge pull request 'feature: výsledková tabulka na konci hry' (#24) from feature/vysledky into version/v0.2.0
Reviewed-on: david/kostky#24
2026-06-27 17:51:50 +02:00
David Spáčil c8a31ac6be fix: oprava anotace 2026-06-27 17:29:41 +02:00
David Spáčil 7b0c9cadbb feature/vysledky: definice funkce win() a její volání z bloku win ve funkci game() 2026-06-27 17:15:47 +02:00
David Spáčil bdd66379f7 feature/vysledky: list z to_sorted_tuple() se vrací s číslováním pořadí 2026-06-27 17:11:10 +02:00
David Spáčil c3a2e94cab feature/vysledky: funkce pro řazení výsledku na základě kódu v check_win(); kód v check_win() nahrazen funkcí 2026-06-27 16:58:10 +02:00
david-spacil 485f152a41 Merge pull request 'Refactoring: typové anotace a úklid' (#23) from refactoring into version/v0.2.0
Reviewed-on: david/kostky#23
2026-06-27 12:59:05 +02:00
David Spáčil 26ad8508b5 refactoring: drobné úpravy UI 2026-06-27 12:55:51 +02:00
David Spáčil 2f8d5b12c2 refactoring: čištění 2026-06-27 12:34:04 +02:00
David Spáčil 5aa5beff69 refactoring: oprava tuple[Any, ...] anotace 2026-06-27 12:30:47 +02:00
David Spáčil a58487e9a6 refactoring: type annotation 2026-06-27 12:23:28 +02:00
David Spáčil 9967682373 refactoring: ve funkci evaluate sloučeny přebytečné returny 2026-06-27 09:24:56 +02:00
david-spacil c225161374 Merge pull request 'Vynulování' (#19) from feature/vynulovani into version/v0.2.0
Reviewed-on: david/kostky#19
2026-06-26 20:40:47 +02:00
david-spacil e7c8d99c96 feature/vynulovani: přidána kondolenční hláška 2026-06-26 20:37:38 +02:00
david-spacil cef9468ad0 feature/vynulovani: přidán ukazatel pčtu "striků" v podobě jednoho až dvou X vedle celkového skóre hráče 2026-06-26 20:33:33 +02:00
david-spacil 0682ad88ce feature/vynulovani: byla implementována logika pro vynulování: po třetím tahu končícím nulou dojde k vynulování celkového skóre hráče 2026-06-26 20:21:08 +02:00
david-spacil a6a256992e Merge pull request 'Přidání možnosti exit' (#18) from feature/exit into version/v0.2.0
Reviewed-on: david/kostky#18
2026-06-26 20:02:35 +02:00
david-spacil 1fcf01152d feature/exit: loučící hláška a konec 2026-06-26 20:00:30 +02:00
david-spacil 2fa0aa8a95 feature/exit: přidáno elif do evaluate() 2026-06-26 19:59:05 +02:00
david-spacil 73e8747b73 feature/exit: přidána možnost do herní nabídky 2026-06-26 19:47:20 +02:00
david-spacil 14f1ebbd29 Merge pull request 'Odstraněno volání funkce clear() při renderování chybových hlášek souvisejících s hodem (je vidět hodnoty na kostkách)' (#16) from fix/chybove-hlasky into version/v0.2.0
Reviewed-on: david/kostky#16
2026-06-26 19:45:13 +02:00
david-spacil 7e3639ab12 fix/chybove-hlasky: odstraněno volání funkce clear() při renderování chybových hlášek souvisejících s hodem (je vidět hodnoty na kostkách) 2026-06-26 19:43:06 +02:00
9 changed files with 512 additions and 298 deletions
+5 -1
View File
@@ -1,2 +1,6 @@
__pycache__/
*.pyc
*.pyc
dist/
build/
*.egg-info/
test.py
+51
View File
@@ -0,0 +1,51 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Working style
- During code review and code discussions, do NOT present ready-made code
proposals or concrete snippets. Limit yourself to advice and recommendations
(what is wrong, why, and the direction of the fix). Only write concrete code
when the user explicitly asks for it.
## 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.)
View File
+4
View File
@@ -0,0 +1,4 @@
from kostky.kostky import main
if __name__ == "__main__":
main()
-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
+374
View File
@@ -0,0 +1,374 @@
import csv
import re
import subprocess
from collections import Counter
from importlib.resources import files
from os import name
from random import randint
from typing import Any
from tabulate import tabulate
hodnoty: dict[str, int] = {}
players: dict[str, list[int]] = {}
pointer: str = ""
first: str = ""
aggr: dict[str, int] = {}
final_score: int = 10000
csv_text = files(__package__).joinpath("hodnoty.csv").read_text(encoding="utf-8")
csv_reader = csv.reader(csv_text.splitlines())
for row in csv_reader:
key = row[0]
value = int(row[1])
hodnoty[key] = value
hodnoty = dict(sorted(hodnoty.items(), key=lambda x: x[1], reverse=True))
def throw(count: int) -> str:
new_throw: list[str] = []
for _dice in range(count):
new_throw.append(str(randint(1, 6)))
new_throw_str: str = "".join(new_throw)
return new_throw_str
def show_throw(throw: str) -> str:
throw_temp: list[str] = []
for x in throw:
throw_temp.append(x)
pretty_list: str = ", ".join(throw_temp)
pretty_list += "\n"
for _i in range(1, len(throw_temp) + 1):
pretty_list += "^ "
pretty_list += "\n"
for i in range(len(throw_temp)):
pretty_list += f"{chr(97+i)} "
return pretty_list
def clear() -> None:
_ = subprocess.call('cls', shell=True) if name == 'nt' else subprocess.call('clear', shell=True)
def translate_letters_to_numbers(inp_hod: str, count: int) -> tuple[bool, str]:
inp_temp: str = ""
for x in inp_hod:
x_int: int = ord(x)-96
if x_int > count:
ran = "1" if count == 1 else f"1{count}"
return False, f"Zvolte prosím kostky v platném rozsahu ({ran}): "
else:
inp_temp += str(x_int)
return True, inp_temp
def pick_throw_proc(count: int, inp_hod: str, throw: str) -> tuple[str, str]:
old_throw: list[str] = []
pick: list[str] = []
for y in range(count):
if str(y+1) not in inp_hod:
old_throw.append(throw[y])
if str(y+1) in inp_hod:
pick.append(throw[y])
old_throw_str: str = "".join(old_throw)
pick.sort()
pick_str: str = "".join(pick)
return old_throw_str, pick_str
def eval_score(pick: str) -> int:
score = 0
pick_counter = Counter(pick)
while pick_counter:
bool_temp = False
for k in hodnoty:
k_counter = Counter(k)
if k_counter <= pick_counter:
pick_counter = pick_counter - k_counter
score += hodnoty[k]
bool_temp = True
if not bool_temp:
break
if pick_counter:
score = 0
return score
def eval_wrapper(count: int, throw: str, inp_hod: str) -> tuple[bool, int, int, str]:
if not inp_hod:
data = (
True,
0,
count,
"",
)
elif re.search(r"^[a-f]+$", inp_hod):
if len(inp_hod) > count:
data = (
False,
0,
count,
"Tolika kostkami nemůžete hodit. Zkuste to znovu: ",
)
else:
tltn = translate_letters_to_numbers(inp_hod, count)
if tltn[0]:
inp_hod = tltn[1]
old_throw_str, pick_str = pick_throw_proc(count, inp_hod, throw)
new_count: int = len(old_throw_str)
score = eval_score(pick_str)
if not score:
new_count = count
old_throw_str = throw
data = (
True,
score,
new_count,
"",
)
else:
data = (
False,
0,
count,
tltn[1]
)
else:
data = (
False,
0,
count,
"Zadejte prosím platnou hodnotu: ",
)
return data
def evaluate(inp_akce: str, count: int, score: int) -> tuple[Any, ...]:
def next_player() -> None:
global pointer
keys_iter = iter(players)
for key in keys_iter:
if key == pointer:
nxt_key = next(keys_iter, first)
break
pointer = nxt_key
if inp_akce == "h":
data = ("count", count, score)
elif inp_akce == "k" or not inp_akce:
if score == 0:
last_two = players[pointer][-2:]
if len(last_two) > 1:
last_two_sum = sum(last_two)
if last_two_sum == 0:
score = -sum(players[pointer])
clear()
input("Po třech nulových hodech bylo vaše skóre vynulováno.")
players[pointer].append(score)
aggr[pointer] = sum(players[pointer])
next_player()
data = ("count", 6, 0)
if pointer == first:
winner: str = check_win()
if winner:
data = ("win", winner, aggr[winner])
elif inp_akce == "x":
data = ("exit",)
return data
def check_value(throw: str) -> bool:
throw_s: list[str] = sorted(throw)
throw_str: str = "".join(throw_s)
hodnota: bool = False
for h in hodnoty:
if h in throw_str:
hodnota = True
break
return hodnota
def add_players() -> None:
global pointer, first, final_score
while True:
inp: str = input("Zadejte jméno prvního hráče: ")
if inp == "":
print("Jméno nemůže být prázdné.")
else:
players[inp] = []
aggr[inp] = 0
pointer = inp
first = inp
print(f"Do seznamu přidáno jméno {inp}.")
break
while True:
inp = input("Zadejte jméno dalšího hráče nebo [x] pro ukončení zadávání hráčů: ")
if inp == "":
print("Jméno nemůže být prázdné.")
elif inp == "x":
break
elif inp in players:
print("Zadávejte, prosím, unikátní jména.")
else:
players[inp] = []
aggr[inp] = 0
print(f"Do seznamu přidáno jméno {inp}.")
while True:
inp = input("Zadejte finální skóre (nechte prázdné pro výchozích 10 000): ")
if inp != "":
try:
inp_int: int = int(inp)
final_score = inp_int
except ValueError:
print("Zadejte prosím platnou číselnou hodnotu.")
else:
break
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[int, str, int]] = []
if s:
d = dict(sorted(d.items(), key=lambda x: x[1], reverse=True))
for i, (k, v) in enumerate(d.items()):
L.append((i+1, k, v))
return L, d
def check_win() -> str:
over_limit: dict[str, int] = {}
for p in aggr:
if aggr[p] >= final_score:
over_limit[p] = aggr[p]
winner: str = ""
if over_limit:
L, d = to_sorted_tuple(over_limit)
count: Counter = Counter(list(d.values()))
for c in count:
if count[c] == 1:
winner = L[0][1]
break
return winner
def win(winner: str, score: int) -> None:
results, d = to_sorted_tuple(aggr)
headers = ["Pořadí", "Hráč", "Skóre"]
table = tabulate(results, headers=headers, tablefmt="fancy_grid")
print(f"Vítězem se stává {winner} s {score} body!")
print("Díky za hru a zase příště.\n")
print(table)
def game(data: tuple) -> None:
while True:
if data[0] == "count":
count: int = data[1]
score: int = data[2]
if not count:
count = 6
th = throw(count)
clear()
strikes: str = ""
if len(players[pointer]) > 0:
if players[pointer][-1:][0] == 0:
strikes = "x"
if players[pointer][-2:][0] == 0 and len(players[pointer]) > 1:
strikes += "x"
else:
strikes = ""
print(f"Na tahu je {pointer} ({aggr[pointer]}{strikes}/{final_score}).")
print(show_throw(th))
if not check_value(th) and count != 0:
score = 0
input("Oh no... anyway.")
data = evaluate("k", count, score)
else:
if count == 0:
count = 6
inp_hod: str = input("Vyberte kostky k vyhodnocení: ")
while True:
data = eval_wrapper(count, th, inp_hod)
if not data[0]:
inp_hod = input(data[3])
continue
score_temp = data[1]
print("Hodnota hodu:", score_temp)
add = ("Hodnota je nulová. Ukončit kolo a přičíst "
f"{score} bodů? ") if not score_temp else ""
while True:
inp = input(f"Potvrdit výběr? {add}[a]no/[n]e: ")
if inp in ["a", "y", "n"]:
break
if inp in ["a", "y"]:
count = data[2]
score += score_temp
break
inp_hod = input("Vyberte kostky k vyhodnocení: ")
while True:
if not score_temp:
inp_akce: str = "k"
else:
inp_akce = input(
f"[h]odit znovu\nu[k]ončit tah a přičíst {score} bodů\ne[x]it?\n"
)
if inp_akce in ["h", "k", "x"]:
break
else:
print("Zadejte prosím platnou hodnotu [h/k/x]. ")
data = evaluate(inp_akce, count, score)
elif data[0] == "win":
winner: str = data[1]
score = data[2]
clear()
win(winner, score)
break
elif data[0] == "exit":
clear()
print("Hra byla předčasně ukončena. Snad se zase brzy uvidíme.")
break
def main() -> None:
clear()
print("Vítejte ve hře v kostky!")
add_players()
data = ("count", 6, 0)
game(data)
if __name__ == "__main__":
main()
-274
View File
@@ -1,274 +0,0 @@
from typing import Any
from random import randint
from os import system, name
import re
import csv
from collections import Counter
score = 0
hodnoty = {}
players = {}
pointer = ""
first = ""
h_bool = True
aggr = {}
final_score = 10000
with open("hodnoty.csv", "r") as csv_file:
csv_reader = csv.reader(csv_file)
for row in csv_reader:
key = str(row[0])
value = int(row[1])
hodnoty[key] = value
def throw(count: int) -> str:
count = int(count)
new_throw = []
for dice in range(count):
new_throw.append(str(randint(1, 6)))
new_throw = "".join(new_throw)
return new_throw
def show_throw(throw: str) -> str:
throw_temp = []
for x in throw:
throw_temp.append(x)
pretty_list = ", ".join(throw_temp)
pretty_list += "\n"
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} "
return pretty_list
def clear():
if name == 'nt':
_ = system('cls')
else:
_ = system('clear')
def evaluate(inp: Any, count: int, throw: str) -> tuple:
global h_bool
def eval_score(pick: str) -> bool:
if(pick in hodnoty):
global score
score += hodnoty[pick]
return True
else:
return False
def next_player() -> None:
global pointer
keys_iter = iter(players)
for key in keys_iter:
if key == pointer:
nxt_key = next(keys_iter, first)
break
pointer = nxt_key
inp = str(inp)
if re.search(r"^\d+$", inp):
if len(inp) > count:
data = ("msg", "ER01", "Tolika kostkami nemůžete hodit. Zkuste to znovu:", count, throw)
return data
else:
for x in inp:
x = int(x)
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)
return data
old_throw = []
pick = []
for y in range(count):
if str(y+1) not in inp:
old_throw.append(throw[y])
if str(y+1) in inp:
pick.append(throw[y])
old_throw = "".join(old_throw)
pick.sort()
pick = "".join(pick)
new_count = len(old_throw)
if not eval_score(pick):
new_count = count
old_throw = throw
else:
h_bool = False
data = ("count", new_count, old_throw)
return data
elif inp == "h":
if h_bool:
clear()
input("Nemůžete házet znovu, dokud nevyhodnotíte některé kostky.")
data = ("count", count, throw)
else:
h_bool = True
data = ("count", count, "")
return data
elif inp == "k":
global score
players[pointer].append(score)
aggr[pointer] = sum(players[pointer])
score = 0
h_bool = True
next_player()
data = ("count", 6, "")
if pointer == first:
winner = check_win()
if winner:
data = ("win", winner, aggr[winner])
return data
else:
data = ("msg", "ER03", "Zadejte prosím platnou hodnotu:", count, throw)
return data
def check_value(throw: str) -> bool:
throw_s = sorted(throw)
throw_str = "".join(throw_s)
hodnota = False
for h in hodnoty:
if h in throw_str:
hodnota = True
break
return hodnota
def add_players():
global pointer, first, final_score
while True:
inp = input("Zadejte jméno prvního hráče: ")
if inp == "":
print("Jméno nemůže být prázdné.")
else:
players[inp] = []
aggr[inp] = 0
pointer = inp
first = inp
print(f"Do seznamu přidáno jméno {inp}.")
break
while True:
inp = input("Zadejte jméno dalšího hráče nebo [x] pro ukončení zadávání hráčů: ")
if inp == "":
print("Jméno nemůže být prázdné.")
elif inp == "x":
break
elif inp in players:
print("Zadávejte, prosím, unikátní jména.")
else:
players[inp] = []
aggr[inp] = 0
print(f"Do seznamu přidáno jméno {inp}.")
while True:
inp = input("Zadejte finální skóre (nechte prázdné pro výchozích 10 000): ")
if inp != "":
try:
inp = int(inp)
final_score = inp
except ValueError:
print("Zadejte prosím platnou číselnou hodnotu.")
else:
break
else:
break
def check_win() -> str:
over_limit = {}
for p in aggr:
if aggr[p] >= final_score:
over_limit[p] = aggr[p]
winner = ""
if over_limit:
over_limit_sorted = dict(sorted(over_limit.items(), key=lambda x: x[1], reverse=True))
over_limit_sorted_list = []
for ols in over_limit_sorted:
over_limit_sorted_list.append((ols, over_limit_sorted[ols]))
count = Counter(list(over_limit_sorted.values()))
for c in count:
if count[c] == 1:
winner = over_limit_sorted_list[0][0]
break
return winner
def game(data: tuple):
while True:
if data[0] == "count":
count = data[1]
th = data[2]
if(th == ""):
th = throw(count)
clear()
print(f"Vítězné skóre: {final_score}.")
print(f"Na tahu je {pointer} ({aggr[pointer]}).")
print(show_throw(th))
if not check_value(th) and data[2] == "" and count != 0:
global score
score = 0
input("Oh no... anyway.")
data = evaluate("k", count, th)
else:
if count == 0:
count = 6
msg = ("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"
" - Hodit znovu: [h].\n"
f" - Ukončit hod a ponechat si skóre z tohoto hodu ({score}): [k].\n"
)
inp = input(msg)
data = evaluate(inp, count, th)
elif data[0] == "msg":
if data[1] == "ER01" or data[1] == "ER02" or data[1] == "ER03":
msg = data[2]
count = data[3]
th = data[4]
clear()
inp = input(msg)
data = evaluate(inp, count, th)
else:
msg = data[1]
print(msg)
break
elif data[0] == "win":
winner = data[1]
score = data[2]
clear()
print(f"Vítězem se stává {winner} s {score} body!")
print("Díky za hru a zase příště.")
break
def main():
clear()
print("Vítejte ve hře v kostky!")
add_players()
input("Prosím, stiskněte enter pro první hod:")
data = ("count", 6, "")
game(data)
if __name__ == "__main__":
main()
+50
View File
@@ -0,0 +1,50 @@
[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"
[project]
name = "kostky"
dynamic = ["version"]
description = "Textová hra v kostky."
readme = "README.md"
requires-python = ">=3.9"
license = "GPL-3.0-or-later"
license-files = ["LICENSE"]
authors = [{ name = "David Spáčil" }]
keywords = ["hra", "kostky", "cli"]
classifiers = [
"Programming Language :: Python :: 3",
"Environment :: Console",
"Operating System :: OS Independent",
"Topic :: Games/Entertainment",
]
dependencies = [
"tabulate",
]
[project.optional-dependencies]
dev = ["mypy", "ruff", "types-tabulate"]
[project.urls]
Repository = "https://gitea.spacilovi.eu/david/kostky"
[project.scripts]
kostky = "kostky.kostky:main"
[tool.hatch.version]
source = "vcs"
[tool.hatch.build.targets.wheel]
packages = ["kostky"]
[tool.mypy]
python_version = "3.10"
strict = true
files = ["kostky"]
[tool.ruff]
target-version = "py39"
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]