Merge pull request 'release: v0.1.0' (#14) from version/v0.1.0 into main

Reviewed-on: david/kostky#14
This commit was merged in pull request #14.
This commit is contained in:
2026-06-25 14:08:56 +02:00
3 changed files with 368 additions and 12 deletions
+92
View File
@@ -1,2 +1,94 @@
# 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í.
## 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`)
## Spuštění
```bash
git clone https://gitea.spacilovi.eu/david/kostky.git
cd kostky
python main.py
```
> 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.
## Jak se hraje
### Začátek hry
1. Zadej jméno prvního hráče (povinné, nesmí být prázdné).
2. Přidávej další hráče; zadáním `x` zadávání ukončíš. Jména musí být unikátní.
3. Zadej cílové skóre, nebo nech pole prázdné pro výchozích **10 000**.
### 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:
```
Vítězné skóre: 10000.
Na tahu je Anna (0).
1, 5, 3, 2, 6, 4
^ ^ ^ ^ ^ ^
1 2 3 4 5 6
```
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. |
| `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. |
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 (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.
## 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ží.
### Stejné kostky
| Kostka | ×1 | ×2 | ×3 | ×4 | ×5 | ×6 |
| --- | --- | --- | --- | --- | --- | --- |
| **1** | 100 | 200 | 1000 | 2000 | 4000 | 8000 |
| **2** | | | 200 | 400 | 800 | 1600 |
| **3** | | | 300 | 600 | 1200 | 2400 |
| **4** | | | 400 | 800 | 1600 | 3200 |
| **5** | 50 | | 500 | 1000 | 2000 | 4000 |
| **6** | | | 600 | 1200 | 2400 | 4800 |
(„–“ = daná kombinace samostatně neboduje.)
### Postupky
| Kombinace | Body |
| --- | --- |
| 1-2-3-4-5 | 750 |
| 2-3-4-5-6 | 1000 |
| 1-2-3-4-5-6 | 1500 |
## Licence
Projekt je licencovaný pod [GPL-3.0](LICENSE).
+30
View File
@@ -0,0 +1,30 @@
111111,8000
666666,4800
11111,4000
555555,4000
444444,3200
66666,2400
333333,2400
1111,2000
55555,2000
44444,1600
222222,1600
123456,1500
6666,1200
33333,1200
111,1000
5555,1000
23456,1000
4444,800
22222,800
12345,750
666,600
3333,600
555,500
444,400
2222,400
333,300
11,200
222,200
1,100
5,50
1 111111 8000
2 666666 4800
3 11111 4000
4 555555 4000
5 444444 3200
6 66666 2400
7 333333 2400
8 1111 2000
9 55555 2000
10 44444 1600
11 222222 1600
12 123456 1500
13 6666 1200
14 33333 1200
15 111 1000
16 5555 1000
17 23456 1000
18 4444 800
19 22222 800
20 12345 750
21 666 600
22 3333 600
23 555 500
24 444 400
25 2222 400
26 333 300
27 11 200
28 222 200
29 1 100
30 5 50
+246 -12
View File
@@ -1,20 +1,47 @@
from typing import Any
from random import randint
from os import system, name
import re
import csv
from collections import Counter
def throw(count):
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):
pretty_list = ", ".join(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) + 1):
for i in range(1, len(throw_temp) + 1):
pretty_list += "^ "
pretty_list += "\n"
for i in range(1, len(throw) + 1):
for i in range(1, len(throw_temp) + 1):
pretty_list += f"{i} "
return pretty_list
@@ -24,17 +51,224 @@ def clear():
else:
_ = system('clear')
def game():
new_throw = throw(6)
clear()
print(show_throw(new_throw))
input("Prosím, stiskněte enter pro další hod:")
game()
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:")
game()
data = ("count", 6, "")
game(data)
if __name__ == "__main__":
main()