diff --git a/README.md b/README.md index de96784..c240e8c 100644 --- a/README.md +++ b/README.md @@ -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). \ No newline at end of file diff --git a/hodnoty.csv b/hodnoty.csv new file mode 100644 index 0000000..bcccc97 --- /dev/null +++ b/hodnoty.csv @@ -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 \ No newline at end of file diff --git a/main.py b/main.py index b24ba74..ffefad1 100644 --- a/main.py +++ b/main.py @@ -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()