Mastermind v Jave: Naprogramuj si logickú hru od nuly

Mastermind je logická hra, ktorá precvičí analytické myslenie a strategické plánovanie. V Jave si ju naprogramujeme od nuly – od generovania tajnej kombinácie farieb až po spätnú väzbu cez ANSI escape kódy. Je to praktická príležitosť precvičiť si cykly, kolekcie a spracovanie vstupu v reálnom projekte.

Drevená hra Mastermind s farebnými kolíkmi pri notebooku s otvoreným kódom.
Hra Mastermind

V článku sa dozvieš:

    Mastermind: pravidlá a logika hry

    Mastermind je hra, kde jeden hráč háda tajnú kombináciu farieb – pričom záleží na poradí. Hru hrávajú najčastejšie dvaja hráči, alebo jeden hráč proti počítaču. Tajná kombinácia obsahuje v klasickej verzii štyri farby zo šiestich možných. Existujú aj viacmiestne verzie – napríklad hra Logik, ktorá je populárna na Slovensku a v Čechách, používa päťmiestny kód z ôsmich možných farieb.

    Po každom pokuse hráč dostane spätnú väzbu vo forme kolíkov:

    • čierne kolíky: správna farba na správnej pozícii,
    • biele kolíky: správna farba na nesprávnej pozícii,
    • bez kolíka: farba ani pozícia nesedí.

    Čierny ani biely kolík neprezradí, ktorú konkrétnu farbu hráč tipol správne – to musí odvodiť sám. Hra rozvíja pamäť a schopnosť systematicky vylučovať možnosti na základe predchádzajúcich pokusov.

    Hra sa končí, keď hráč uhádne všetky štyri farby na správnych miestach. Hru Mastermind vynašiel v roku 1970 Mordecai Meirowitz, izraelský poštmajster a odborník na telekomunikácie. Po predaji nápadu sa hra rozšírila do celého sveta – dnes existuje viac ako 20 verzií.

    Vieš, že…

    …matematická analýza ukázala, že ideálnu kombináciu v klasickej verzii (4 farby zo 6 možných) je možné uhádnuť v maximálne piatich ťahoch, ak sa používa optimálna stratégia? V našej implementácii máš k dispozícii 10 pokusov.

    Koncept našej implementácie

    Vytvoríme klasickú verziu hry Mastermind v Jave. Počítač si zvolí tajnú kombináciu štyroch farieb zo šiestich možných, každú použije maximálne raz. Hráč má 10 kôl – v každom zadá štvorznakovú kombináciu. Každá farba je identifikovaná písmenom:

    • R = Red (červená)
    • G = Green (zelená)
    • B = Blue (modrá)
    • Y = Yellow (žltá)
    • C = Cyan (azurová)
    • M = Magenta (ružová)

    Počítač v rámci každého kola poskytne spätnú väzbu vo forme čiernych a bielych kolíkov. Pre farebnú reprezentáciu na konzole použijeme ANSI escape kódy – každú farbu zobrazíme ako medzeru s farebným pozadím. Hra pokračuje až kým hráč neuhádne kombináciu, alebo nevyčerpá všetky pokusy.

    Recommend

    Odporúčame ti…

    Kompletný kód nájdeš v súbore – stiahnuť zdrojové kódy hry Mastermind.

    Implementácia hry Mastermind v Jave

    Program je rozdelený do dvoch tried. Hlavná biznis logika je v triede Mastermind, vstupný bod programu v triede Main. Spolu pokrývajú celý herný cyklus od generovania tajnej kombinácie až po vyhodnotenie výsledku.

    Trieda Mastermind

    Hlavná trieda obsahuje všetky metódy potrebné na riadenie herného cyklu: generovanie tajnej kombinácie, validáciu vstupov, porovnávanie tipov a zobrazenie spätnej väzby.

    package games;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Random;
    import java.util.Scanner;
    public class Mastermind {
        // Game settings
        private static final int CODE_LENGTH = 4;
        private static final int MAX_ATTEMPTS = 10;
        // Define colors and their ANSI escape codes
        private static final char[] COLORS = {'R', 'G', 'B', 'Y', 'C', 'M'};
        private static final String[] COLOR_CODES = {
                "\u001B[41m", // Red
                "\u001B[42m", // Green
                "\u001B[44m", // Blue
                "\u001B[43m", // Yellow
                "\u001B[46m", // Cyan
                "\u001B[45m"  // Magenta
        };
        private static final String COLOR_RESET = "\u001B[0m";
        private static final String BLACK_PEG = "\u001B[40m   \u001B[0m"; // Black
        private static final String WHITE_PEG = "\u001B[107m   \u001B[0m"; // Grey
        private List<Character> secretCombination;
        public Mastermind() {
            this.secretCombination = generateSecretCombination();
        }
        public void startGame() {
            Scanner scanner = new Scanner(System.in);
            int attempts = 0;
            boolean won = false;
            System.out.println("Welcome to Mastermind! Try to guess the secret combination of " + CODE_LENGTH + " colors.");
            displayColorLegend();
            while (attempts < MAX_ATTEMPTS && !won) {
                System.out.print("\nEnter your (" + CODE_LENGTH + " letters) guess: ");
                String input = scanner.nextLine().toUpperCase();
                if (input.length() != CODE_LENGTH || !isValidInput(input)) {
                    System.out.println("Invalid input! Please enter exactly " + CODE_LENGTH + " letters from the available colors.");
                    continue;
                }
                List<Character> guess = new ArrayList<>();
                for (char c : input.toCharArray()) {
                    guess.add(c);
                }
                int correctPosition = 0;
                int correctColor = 0;
                List<Character> tempSecret = new ArrayList<>(secretCombination);
                List<Character> tempGuess = new ArrayList<>(guess);
                // Count correct positions
                for (int i = 0; i < CODE_LENGTH; i++) {
                    if (guess.get(i).equals(tempSecret.get(i))) {
                        correctPosition++;
                        tempSecret.set(i, null);
                        tempGuess.set(i, null);
                    }
                }
                // Count correct colors in wrong positions
                for (int i = 0; i < CODE_LENGTH; i++) {
                    if (tempGuess.get(i) != null && tempSecret.contains(tempGuess.get(i))) {
                        correctColor++;                    tempSecret.set(tempSecret.indexOf(tempGuess.get(i)), null);
                    }
                }
                // Display feedback with color pegs
                System.out.print(formatGuess(guess) + " => ");
                for (int i = 0; i < correctPosition; i++) {
                    System.out.print(BLACK_PEG + " ");
                }
                for (int i = 0; i < correctColor; i++) {
                    System.out.print(WHITE_PEG + " ");
                }
                System.out.println();
                if (correctPosition == CODE_LENGTH) {
                    won = true;
                    System.out.println("\nCongratulations! You guessed the correct combination: " + formatGuess(secretCombination));
                } else {
                    attempts++;
                }
            }
            if (!won) {
                System.out.println("\nYou lost! The correct combination was: " + formatGuess(secretCombination));
            }
        }
        private List<Character> generateSecretCombination() {
            Random random = new Random();
            List<Character> availableColors = new ArrayList<>();
            for (char color : COLORS) {
                availableColors.add(color);
            }
            // Shuffle available colors
            Collections.shuffle(availableColors, random);
            return availableColors.subList(0, CODE_LENGTH);
        }
        private boolean isValidInput(String input) {
            for (char c : input.toCharArray()) {
                boolean valid = false;
                for (char color : COLORS) {
                    if (c == color) {
                        valid = true;
                        break;
                    }
                }
                if (!valid) return false;
            }
            return true;
        }
        private void displayColorLegend() {
            System.out.print("Available colors:");
            for (int i = 0; i < COLORS.length; i++) {
                System.out.print(COLOR_CODES[i] + " " + COLORS[i] + " " + COLOR_RESET);
            }
            System.out.println();
        }
        private String formatGuess(List<Character> guess) {
            StringBuilder formatted = new StringBuilder();
            for (Character color : guess) {
                int index = new String(COLORS).indexOf(color);
                if (index != -1) {                formatted.append(COLOR_CODES[index]).append("   ").append(COLOR_RESET).append("");
                } else {
                    formatted.append(color).append("");
                }
            }
            return formatted.toString();
        }
    }
    

    Vstupná trieda vytvorí inštanciu hry a spustí ju. Ide o minimalistický kód, ktorý volá metódustartGame().

    import games.Mastermind;
    public class Main {
        public static void main(String[] args) {
            Mastermind game = new Mastermind();
            game.startGame();
        }
    }
    

    Ako hra funguje: päť kľúčových častí

    Logiku hry Mastermind Java môžeme rozdeliť do piatich funkcionálnych celkov. Každý z nich zabezpečuje jednu konkrétnu časť herného cyklu.

    #1 Nastavenie hry

    Hra pracuje s tromi konfiguračnými parametrami definovanými ako konštanty:

    • Dĺžka kódu – kombinácia pozostáva zo 4 farieb (konštanta (CODE_LENGTH))
    • Počet pokusov – hráč má maximálne 10 pokusov (konštanta (MAX_ATTEMPTS))
    • Dostupné farby – farby sú reprezentované znakmi (R, G, B, Y, C, M) a každá má svoj ANSI escape kód, ktorý umožňuje farebný výstup na termináli.

    #2 Generovanie tajnej kombinácie

    Metóda generateSecretCombination() vytvorí tajnú kombináciu. Najprv zamieša všetky dostupné farby a potom vyberie prvé štyri z nich (bez opakovania farieb). Tajná kombinácia sa uloží do zoznamu secretCombination.

    #3 Overenie vstupu a začiatok hry

    V metóde startGame() program zobrazí uvítaciu správu a zoznam dostupných farieb. Hráč zadáva tipy vo forme reťazca (napr. RGBY). Funkcia isValidInput() kontroluje, či zadaný kód obsahuje len platné farby v správnom počte. Ak nie, program vyzve hráča na opakovanie.

    #4 Porovnávanie tipu s tajnou kombináciou

    Pre každý tip program vypočíta dve hodnoty: počet správne umiestnených farieb (Black Peg) a počet správnych farieb na nesprávnej pozícii (White Peg). Program najprv identifikuje všetky správne pozície, zo zvyšných farieb následne vypočíta prienik oboch zoznamov. Výsledky sa zobrazia ako farebná spätná väzba po každom pokuse.

    #5 Kontrola výhry a zobrazenie výsledku

    Ak hráč uhádne všetky farby na správnych miestach (correctPosition == CODE_LENGTH), hra sa končí výhrou a program zablahoželá. Ak po maximálnom počte pokusov kombinácia ešte nie je uhádnutá, program zobrazí správnu kombináciu.

    Výstup jednej hry môže vyzerať nasledovne:

    Ukážka terminálového výstupu hry Mastermind s farebnými kombináciami a spätnou väzbou.
    Výstup hry Mastermind v termináli
    Recommend

    Odporúčame ti…

    Ak máš vo vývojovom prostredí čiernu konzolu, zmeň si ANSI kód čiernej farby na inú – inak farebné štvorčeky splynú s pozadím a spätná väzba nebude viditeľná.

    FAQ: Najčastejšie kladené otázky o Mastermind v Jave

    Môžem v hre používať opakované farby v tajnej kombinácii?

    V našej implementácii nie – každá farba sa vyskytuje maximálne raz. Ak chceš povoliť opakovanie, stačí upraviť metódu generateSecretCombination() tak, aby nevyužívala zamiešavanie, ale náhodný výber s vrátením.

    Prečo sa používajú ANSI escape kódy?

    ANSI escape kódy sú štandardný spôsob, ako meniť farbu textu alebo pozadia v termináli na Unix, Mac aj Windows (od verzie 10). Každý kód je reťazec vo formáte \033[Xm, kde X je číslo farby. Počítač ich interpretuje priamo pri výstupe na terminál.

    Ako program správne počíta biele kolíky?

    Program najprv odpočíta všetky správne pozície (Black Peg). Zo zvyšných farieb tipu a tajnej kombinácie potom vypočíta prienik – farby, ktoré sa nachádzajú v oboch zoznamoch, ale na iných miestach. Počet týchto spoločných farieb tvorí hodnotu White Peg.

    Dajú sa v Jave naprogramovať aj iné logické hry podobného typu?

    Samozrejme. Podobný prístup (tajný kód, spätná väzba, obmedzenie pokusov) môžeš použiť pre Obesenec, hádanie čísel alebo wordlové hry. Na msgprogramator.sk nájdeš ďalšie tutoriály, napríklad Hádaj číslo, Obesenec a Kameň-papier-nožnice.

    6 min.Programovanie hier pre začiatočníkov – ako naprogramovať hru v Java: Hádaj číslo

    Programovanie hier pre začiatočníkov – ako naprogramovať hru v Java: Hádaj číslo

    V článku sa dozvieš, ako naprogramovať hru Java pre začiatočníkov – Hádaj číslo. Inštrukcie krok za krokom s Java kódom.

    Čo ťa Mastermind v Jave naučí

    Hra Mastermind v Jave je viac ako zábava – v jednom projekte pokrýva základné koncepty OOP: triedy, metódy, kolekcie, cykly a spracovanie vstupu. ANSI kódy pridávajú reálny vizuálny efekt priamo v termináli. Cesta od prázdnej triedy k fungujúcej hre precvičí dosť na to, aby ostalo v pamäti.

    Odporúčame ti sa s kódom ďalej pohrať a precvičiť si svoje Java znalosti. Hádanie farebných kombinácií môžeš uľahčiť, či naopak sťažiť. Napríklad môžeš urobiť variant hry Super Mastermind s kombináciou piatich farieb z ôsmich možných. Alebo do našej klasickej verzie doprogramovať možnosť opakovať farby v tajnej kombinácií. Pokročilí programátori môžu do hry vymyslieť algoritmus AI, ktorá bude hádať optimálne kombinácie za hráča na čo najmenší počet ťahov.

    O autorovi

    Jozef Wagner

    Java Developer Senior

    Viac ako 10 rokov programujem v Jave, momentálne pracujem v msg life Slovakia ako Java programátor senior a pomáham zákazníkom implementovať ich požiadavky do poistného softvéru Life Factory. Vo voľnom čase si rád oddýchnem v lese, prípadne si zahrám nejakú dobrú počítačovú hru.

    Daj nám o sebe vedieť