diff --git a/.gitignore b/.gitignore index 067e3b3..ecff981 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ __pycache__/ *.pyc dist/ build/ -*.egg-info/ \ No newline at end of file +*.egg-info/ +test.py \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index fb991a8..1089918 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,6 +2,13 @@ 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). diff --git a/kostky/kostky.py b/kostky/kostky.py index 3dcc268..8518b95 100644 --- a/kostky/kostky.py +++ b/kostky/kostky.py @@ -1,19 +1,18 @@ import csv import re +import subprocess from collections import Counter from importlib.resources import files -from os import name, system +from os import name from random import randint from typing import Any from tabulate import tabulate -score: int = 0 hodnoty: dict[str, int] = {} players: dict[str, list[int]] = {} pointer: str = "" first: str = "" -h_bool: bool = True aggr: dict[str, int] = {} final_score: int = 10000 @@ -21,13 +20,14 @@ 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 = str(row[0]) + 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: - count = int(count) new_throw: list[str] = [] for _dice in range(count): new_throw.append(str(randint(1, 6))) @@ -49,19 +49,110 @@ def show_throw(throw: str) -> str: return pretty_list def clear() -> None: - _ = system('cls') if name == 'nt' else system('clear') + _ = subprocess.call('cls', shell=True) if name == 'nt' else subprocess.call('clear', shell=True) -def evaluate(inp: Any, count: int, throw: str) -> tuple[Any, ...]: - - global h_bool - - def eval_score(pick: str) -> bool: - if(pick in hodnoty): - global score - score += hodnoty[pick] - return 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: - return False + 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 @@ -74,69 +165,10 @@ def evaluate(inp: Any, count: int, throw: str) -> tuple[Any, ...]: pointer = nxt_key - inp = str(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 - ) - else: - for x in inp: - x = ord(x)-96 - if x > count: - 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): - if str(y+1) not in inp: - old_throw.append(throw[y]) - if str(y+1) in inp: - pick.append(throw[y]) - - old_throw_str: str = "".join(old_throw) - pick.sort() - pick_str: str = "".join(pick) - - new_count: int = len(old_throw_str) - - if not eval_score(pick_str): - new_count = count - old_throw_str = throw - else: - h_bool = False - - data = ("count", new_count, old_throw_str) - - 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, "") - - elif inp == "k": - global score + 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: @@ -148,21 +180,16 @@ def evaluate(inp: Any, count: int, throw: str) -> tuple[Any, ...]: players[pointer].append(score) aggr[pointer] = sum(players[pointer]) - score = 0 - h_bool = True next_player() - data = ("count", 6, "") + data = ("count", 6, 0) if pointer == first: winner: str = check_win() if winner: data = ("win", winner, aggr[winner]) - elif inp == "exit": + elif inp_akce == "x": data = ("exit",) - else: - data = ("msg", "ER03", "Zadejte prosím platnou hodnotu:", count, throw) - return data def check_value(throw: str) -> bool: @@ -264,54 +291,65 @@ def game(data: tuple) -> None: while True: if data[0] == "count": count: int = data[1] - th: str = data[2] - if(th == ""): - th = throw(count) + score: int = data[2] + if not count: + count = 6 + th = throw(count) clear() - # print(f"Vítězné skóre: {final_score}.") strikes: str = "" if len(players[pointer]) > 0: if players[pointer][-1:][0] == 0: strikes = "x" - if players[pointer][-2:][0] == 0: + 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 data[2] == "" and count != 0: - global score + if not check_value(th) and count != 0: score = 0 input("Oh no... anyway.") - data = evaluate("k", count, th) + data = evaluate("k", count, score) else: 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ř. 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" - ) - inp: str = input(msg) - data = evaluate(inp, count, th) + 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] == "msg": - if data[1] == "ER01" or data[1] == "ER02" or data[1] == "ER03": - msg = data[2] - count = data[3] - th = data[4] - inp = input(msg) - data = evaluate(inp, count, th) - else: - msg = data[1] - print(msg) - break - elif data[0] == "win": winner: str = data[1] score = data[2] @@ -329,7 +367,7 @@ def main() -> None: clear() print("Vítejte ve hře v kostky!") add_players() - data = ("count", 6, "") + data = ("count", 6, 0) game(data) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index ce6429b..18543a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ source = "vcs" packages = ["kostky"] [tool.mypy] -python_version = "3.9" +python_version = "3.10" strict = true files = ["kostky"]