Kígyókockát készítünk

Kígyókocka

KígyókockaA kígyókocka (snake cube, chain cube) 27 egyforma méretű, egymáshoz képest mozgatható/forgatható kockából áll. A kockákat összeköti egy rugalmas fonal/gumi. Az első és az utolsó kocka egy-egy lapján egy-egy lyuk van. A közbenső kockák hat lapjából kettő lyukas. Fából és műanyagból is készülhetnek. Általában kétféle színnel színezettek a kockák. A cél, hogy a 27 kockát kígyózva „összehajtogatva” a kígyó (lánc) összeálljon egy nagyobb 3x3x3 méretű kockává.

A színek – a játék gyártóitól függően – nehézségi szinteket jelölhetnek (zöld, kék, piros, narancs, lila). Léteznek könnyebben és nehezebben megoldható kígyókockák. Előbbieknél többször fordul elő két egymással szemben lévő lyukas lap a közbenső kockákon, utóbbiaknál gyakoribbak az egymással szomszédos lapokon lévő lyukak. Másképpen: előbbiben több a három hosszú egyenes rész, utóbbi szinte állandóan tekereg. Általában a kocka egyik csúcsából kezdjük a megoldást, de az igazán nehéz játékok esetében a kígyó indulhat akár a kocka egyik lapjának (3×3) középső kockájától is.

Vannak olyan kígyókockák, amelyeknek több megoldása is van, azaz többféleképpen is összeállítható kockává. Ezek részletes ismertetése (típusok, gyártók, színek), a megoldások (statikusan és dinamikusan), irányokat mutató jelölésrendszer (Front, Left, Up, Back, Right, Down) elérhető Jaap Scherphuis – holland puzzle rajongó – weboldalán: Jaap’s Puzzle Page.

Kígyókocka

Olyan Java programot készítünk, amely véletlenszerű kígyókockát állít elő.

Tervezés

Szükséges egy háromdimenziós tömb adatszerkezet a kocka tárolására. Több okból is hasznos, ha a tömb mérete 5x5x5. Egyrészt így indexek 0-tól 4-ig futnak és a tömb közepén lévő 3x3x3-as kocka elemei kényelmesen – mátrixszerűen – indexelhetők 1-től 3-ig. Másrészt a tömb közepén lévő 3x3x3-as kocka minden elemére igaz, hogy egységesen van 26 db érvényesen indexelhető szomszédja. A 125 tömbelemből a széleken lévő 98 elem negatív számokkal feltölthető.

A szokásos i, j, k egységvektor rendszerben (koordináta-rendszerben) gondolkodva, i és j a képernyő síkját, k pedig a mélységet jelenti. A k-val 0-tól 4-ig „szeletelve” a tömböt, öt db négyzetet/mátrixot kapunk az alábbiak szerint. A színes tömbelemek negatív számokkal kerülnek feltöltésre, a kígyó útját határolják mindhárom irányból:

Kígyókocka tervezés

A belül lévő – fehér színű – 3x3x3-as kocka/tömb elemeit kezdőértékként célszerű 0-val feltölteni.

A szomszédos kockák kiválasztása során csak a középen lévő kocka 6 db lapszomszédját kell figyelembe venni. A középen lévő (a következő ábrán nem látszó) kocka három tengely szerinti 2-2-2 szomszédos kockája különböző színekkel jelölt.

Kígyókocka tervezés

Él- és csúcsszomszédság esetén nem tud tekeredni a kígyó. A forduláshoz/tekeredéshez legalább 3 – a kígyóban egymás utáni – kocka szükséges. Az aktuális kockának – pozíciójától függően – legfeljebb 6 lapszomszédja lehet. Ezt csökkenti, ha a kocka valamelyik csúcsban helyezkedik el, illetve menet közben is – ahogyan egyre hosszabb lesz a kígyó – folyamatosan csökken a még szabad elemek száma.

A megoldás során a belül lévő – fehér színű – 3x3x3-as kocka/tömb elemeit kell sorszámozni 1-től 27-ig, jelölve ezzel a kígyó útját. A kezdetben 0-val jelölt szabad elemek végül elfogynak.

Megállapodunk abban, hogy a kígyó az útját az (1, 1, 1) pozícióban kezdi és az 1 sorszámot kapja. Addig kell újabb szomszédos kockákat – egyesével haladva – kiválasztani módszeresen vagy véletlenszerűen próbálkozva, vagy akár visszalépéses algoritmussal is, amíg mind a 27 kiválasztásra kerül.

Megvalósítás

Létre kell hozni a háromdimenziós tömböt példányváltozóként:
int[][][] cube=new int[5][5][5];

A cubeInit() metódus kezdetben feltölti a tömb elemeit. A széleken lévő elemekbe különböző negatív értékek kerülnek, hogy jól megkülönböztethető legyen, melyik ciklus melyik pozíciókért felel. Másképpen is lehetne: például kezdetben minden elem -1, utána a belül lévők felülírhatók 0-val.

Hasznos a cubePlot() metódus, amellyel megjeleníthetők a konzolon a tömb elemei (állapota):

A getNextNeighbour() függvény egydimenziós tömbként ( int[]) visszaadja a paramétereként átvett – x, y, z koordinátával jelölt – kocka egyik kiválasztott szomszédját, amely a kígyó útját jelöli. A kiválasztás történhet módszeresen vagy véletlenszerűen próbálkozva, vagy akár visszalépéses algoritmussal is. A metódus forráskódját most nem részletezem. A metódus felelős a kígyó helyes útvonaláért, azaz a kiválasztás során a kígyó nem rekedhet meg zsákutcában, másképpen nem haraphatja meg saját magát.

A vezérlést a run() metódus végzi el az alábbiak szerint:

Addig fut a ciklus, amíg a kígyó nem tölti ki a 3x3x3-as kockát (másképpen: amíg a kígyó nem éri el a maximális hosszúságot). A tömb állapotát kezdetben és lépésenként is kiíratja a vezérlő metódus a konzolra.

Konzolos eredmény

A konzolos eredmény előállításánál fontos volt, a tömb változásait tudjuk követni. Az összes negatív szám elhagyható lehet a kiírásból, ha meggyőződtünk az implementált algoritmus helyes működéséről. Átlátva a problémát, a megoldás könnyen elállítható egy grafikus és/vagy egy irányokat mutató jelölésrendszer szerint is, például:

Kígyókocka tervezés

Hivatkozások

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam tematikájához kötődik. Több alkalommal is tudunk ezzel a feladattal foglalkozni, attól függően, hogy a getNextNeighbour() függvény működését hogyan tervezzük és implementáljuk:

  • A 13-16. óra: Tömbök témakör esetén a szomszédos kockák közül módszeresen – azonos sorrendben haladva a legfeljebb 6 lehetséges szomszéd közül – választjuk ki mindig az elsőt. Ekkor mindig ugyanazt az egyetlen helyes megoldást kapjuk meg.
  • A 17-28. óra: Objektumorientált programozás témakör esetén atipikusan a kivételkezelést használhatjuk vezérlésre úgy, hogy a lehetséges szomszédos kockák közül mindig véletlenszerűen választunk. Ekkor a kígyó önmagába harapását úgy előzzük meg, hogy tömb túlindexelésekor keletkező kivételt benyeljük és újrakezdjük a feladatot mindaddig, amíg találunk egy olyan megoldást, aminek lépései közben nem keletkezik kivétel.
  • Az orientáló modul 9-12. óra: Mesterséges intelligencia témakör esetén véletlenszerű kocka kiválasztási stratégiával rendelkező visszalépéses algoritmust specifikálhatunk és implementálhatunk. Ez lényegesen összetettebb feladat és mindig helyes megoldást ad több lehetséges megoldás közül.

Húsvétvasárnap

Húsvétvasárnap

HúsvétvasárnapA nyugati kereszténység húsvétvasárnapja legkorábban március 22-ére, legkésőbb április 25-re esik. Másképpen: a húsvét mozgó ünnep, azaz nem esik az év ugyanazon napjára minden évben. Az első niceai zsinat 325-ben úgy határozott, hogy legyen a keresztény húsvét időpontja a tavaszi napéjegyenlőség utáni első holdtöltét követő vasárnap.

A húsvét kiszámítására a legismertebb algoritmus Gauss módszere. A Java implementációban az easterGauss() függvény által elfogadható év paramétert életszerűen lekorlátoztam 1900-2099-ig terjedő évekre, valamint a vezérlés az aktuális és a rákövetkező 19 évben ír ki eredményt:

Az algoritmus részletes magyarázata alapján könnyen kiegészíthető úgy, hogy tetszőleges évre, illetve különböző naptárakra is működjön.

A kapott eredmények megtekinthetők:

A feladat – a kivételkezeléstől eltekintve – a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 5-8. óra: Vezérlési szerkezetek alkalmához kötődik.

Készítettem egy kipróbálható JavaScript változatot is. A csúszkán a kör mozgatásával megkaphatjuk az aktuális és a következő néhány évben a húsvétvasárnap dátumát.

Húsvétvasárnap a megadott évben:

 

Nemzetközi Pi nap

Pi-logo

Pi-logoA Pi-t (π) mindenki ismeri. Talán sokaknak kedvenc története is van a π-vel kapcsolatosan, amelyet iskolában vagy utazásai alatt szerzett. A π Euklidesz geometriájában a kör kerületének és átmérőjének arányát jelöli. A π irracionális szám, azaz végtelen, nem szakaszos tizedestört; másképpen számjegyei között nincs ismétlődés. A π értékével a hétköznapokban 3,14-dal szokás számolni, de a tudomány területén ennél sokkal pontosabb közelítést szokás alkalmazni. A π közelítése az informatikának köszönhetően akár több millió tizedesjegyig is lehetséges (például: S. Memphill: Pi to 1,000,000 places).

A nemzetközi Pi nap alkalmából (március 14) megvalósítottunk néhány – végtelen összeggel és szorzattal – π közelítésre való képletet, algoritmust Java nyelven.

1. Viète-féle sor

Pi-kozelites-Viete

A módszer néhány eredménye: i=5  esetén 3.140331156954752  (2 tizedesjegyre pontos), i=10 -nél 3.1415914215112  (5 tizedesjegyre pontos), i=11  esetén 3.1415923455701176  (6 tizedesjegyre pontos).

2. Leibniz-féle sor

Pi-kozelites-Leibniz

A módszer néhány eredménye: a 24. lépéstől stabil 1 tizedesjegyre, a 626. lépéstől stabil 2. tizedesjegyre, a 2453. lépéstől stabil 3 tizedesjegyre (hiszen alternál).

3. Wallis-formula

Pi-kozelites-Wallis

A módszer néhány eredménye: A 38. lépéstől 1, a 986. lépéstől 2, a 2650. lépéstől 3, a 16954. lépéstől már 4 tizedesjegyre pontos.

4. Csebisev-sor

Pi-kozelites-Csebisev

A módszer k=10 -re már 8 tizedesjegyig pontos.

A bejegyzéshez tartozó teljes forráskódot – további 8 közelítő módszer implementációjával együtt – ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 5-8. óra: Vezérlési szerkezetek alkalmához kötődik.

Telefonos billentyűzettel kódolunk/dekódolunk

Telephone-keypad

Telephone-keypadNem­rég egy be­tű­ket és szá­mo­kat tar­tal­ma­zó kó­dolt szö­ve­get kap­tam azzal a ké­rés­sel, hogy pró­bál­jam meg­fej­te­ni. A tit­ko­sí­tott szö­ve­get a kö­vet­ke­ző for­má­tum­ban kap­tam: 88.222.666.333.444.888.33.333. Azt is le­he­tett ró­la tud­ni, hogy a meg­fej­tés csak az an­gol á­bé­cé be­tű­it és szá­mo­kat tar­tal­maz­hat. Ilyen és ehhez ha­son­ló kó­dok meg­fej­té­se­it az Ingress ne­vű AR (augmented reality) já­ték­ban le­het fel­hasz­nál­ni (Android és iOS plat­for­mon is el­ér­he­tő), ahol a já­ték fej­lesz­tői min­dig va­la­mi­lyen egy­sze­rűbb kó­do­lás­sal juttat­ják el a já­té­ko­sok egy cso­port­ja szá­má­ra a kó­do­kat, ami­ért a já­ték­ban extra fel­sze­re­lés­hez le­het jut­ni. Az elő­ző for­du­lók­ban már ta­lál­koz­tam Base64 és Morse-kódolás­sal is, így gya­ní­tot­tam, hogy a mos­ta­ni felad­vány meg­fej­té­se sem le­het ne­héz fela­dat. Úgy gon­dol­tam, hogy a szá­mok kö­zötti pon­tok egy-egy ka­rak­ter el­vá­lasz­tá­sát je­lent­he­tik, míg a szám­je­gyek da­rab­szá­ma is hor­doz­hat hasz­nos in­for­má­ci­ót, nem csak az ér­té­kük. In­for­ma­ti­kus lé­vén rög­tön az ASCII táb­la ju­tott eszem­be, de bár­hogy pró­bál­tam va­la­mi­lyen le­ké­pe­ző függ­vényt al­kot­ni, nem si­ke­rült a szá­mo­kat le­szű­kí­te­ni a be­tűk és szá­mok tar­to­má­nyá­ra. A vég­ső megol­dást egy csa­pat­tár­sam se­gí­tett meg­ta­lál­ni, aki a jobb ol­da­lon lát­ha­tó ké­pet küld­te el ne­kem.

Például kódoljuk a SZOFTVERFEJLESZTES szöveget és ezt kapjuk: 7777.9999.666.333.8.888.33.777.333.33.5.555.33.7777.9999.8.33.7777, amit dekódolva természetesen visszakapjuk az eredeti szöveget. Hogyan működik mindez?

Tegyük fel, hogy a kódolás és dekódolás során csak az angol ábécé nagybetűit és a szóközt fogjuk használni. Hasznos néhány konstans deklarációja: a nyomógombok feliratai szövegként ( TABLE1) és tömbben ( TABLE2), szeparátorok nélküli ábécé ( TABLE3) a kódolás elvégzéséhez, valamint a dekódoláshoz szükséges szöveg ( TABLE4):

A kódolás (titkosítás) lépései

A kódolás elvégzését ellenőrzésnek kell megelőznie, hiszen a paraméterként átvett szöveg ( text) nem kódolható ha üres ( isEmpty()) vagy érvénytelen karaktert tartalmaz (olyat, ami nem szerepel a telefon nyomógombjain: ékezetes vagy írásjel). Bármilyen probléma esetén a kódoló metódus kivételt dob. A kódolás során a szöveget automatikusan nagybetűsként értelmezzük.
A kódolás során minden karakter (pl.: E) esetén ki kell választani, hogy a TABLE2 tömb melyik elemében szerepel (pl.: j=3, a nyomógomb felirata DEF) és a j-edik elemben tárolt szöveg hányadik pozícióján található (pl.: index=1). Tehát tudjuk, hogy a C karakter kódja 33, azaz ehhez a 3-as gombot kétszer ( index+1) kell lenyomni. A Java nyelvben tömbök indexelése és a szövegben lévő karakterek pozíciója is nulla bázisú sorszámmal történik. A karakterek 1-4 (változó) hosszú kódjai közé pont kerül ( coded).

A dekódolás (visszafejtés) lépései

A dekódolás elvégzését is ellenőrzésnek kell megelőznie, hiszen a paraméterként átvett szöveg ( text) nem dekódolható ha üres ( isEmpty()) vagy érvénytelen karaktert tartalmaz (olyat, ami nem feleltethető meg a telefon nyomógombjain található karakterek egyikének). Bármilyen probléma esetén a dekódoló metódus is kivételt dob.
A dekódolás során minden karakter kódja (pl.: 33) esetén szükség van annak hosszára ( length=2) és első karakterére számként ( index=3). Ezek alapján tudjuk, hogy a TABLE2 tömb index-edik ( DEF) elemének length-1-edik eleme a dekódolt karakter ( E). A dekódoló metódus nem tesz szeparátort a dekódolt karakterek ( decoded) összefűzése során. A változó hosszúságú kódolt szöveg elemeiből egykarakteres dekódolt szövegdarabok keletkeznek.

Az ellenőrzés lépései

A logikai értékkel visszatérő ellenőrző függvény ( isValidText()) feladata eldönteni, hogy a kódolás/dekódolás során használandó szöveg ( text) minden karaktere feldolgozható, azaz a folyamat során értelmezhető (másképpen: a validCharacters szöveg tartalmazza). Optimális esetben a text hossza megegyezik a benne lévő feldolgozható/értelmezhető karakterek számával (végighalad a ciklus a text-en), egyébként leáll a ciklus az első problémás karakternél.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

Ez a feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 21-24. óra: Objektumorientált programozás, 2. rész alkalmához, valamint minden tanfolyamunk orientáló moduljának 1-4. óra: Programozási tételek alkalmához kapcsolódik.

Koch-görbét rajzolunk

Koch-görbe

Koch-görbeA Koch-görbe egyike a legrégebben ismert egyszerű fraktáloknak. Mint ilyen, önhasonlóan rekurzív. Az önhasonlóság azt jelenti, hogy az ábra tetszőleges részét felnagyítva mindig hasonló/ugyanolyan részek jelennek meg (a méretaránytól függetlenül). Az n=1 szinten a Koch-görbe kiindulópontja egy szabályos háromszög. A n+1-edik szinten az n-edik szinten található szakaszokat harmadoljuk, és a középső szakasz helyére egy harmad akkora háromszög két szárát illesztjük (az alapját kihagyjuk). Ezt rekurzívan folytatva kapjuk meg a Koch-görbét, másképpen Koch-féle hópelyhet.

Írtam egy egyszerű Java programot, amely n=1-től 9-ig paraméterezhetően kirajzolja a Koch-görbét egy grafikus felületre. Így működik:

Koch-görbe rajzolását bemutató program működése

A program elkészítéséhez néhány alapvető dolgot kell csupán tudni:

  • Vászontechnikával tudunk swing GUI felületre ( Graphics osztályú g objektum) rajzolni, ahol a koordináta-rendszer origója egy téglalap alakú terület bal felső csúcsa, X jobbra növekszik, Y pedig lefelé növekszik.
  • Kétféle szín áll rendelkezésre: háttérszín (most Color.WHITE), illetve rajzolószín (most Color.BLUE).
  • A rajzoláshoz grafikai primitíveket használhatunk, például pont, szakasz, téglalap, ellipszis. Szakaszt két végpontjának koordinátáival tudunk rajzolni a drawLine() metódussal.
  • Be kell állítani a vászon méreteit, azaz annak a komponensnek ( JPanel-ből öröklött KochPanel osztályú pnKoch objektum) a méreteit, amelyre ráfeszül a vászon.
  • Egy Slider osztályú sSzint nevű vezérlőobjektum ChangeListener figyelőinterfész stateChanged() eseménykezelő metódust implementáló objektumával paraméterezzük a rajzolást 1-től 9-ig.
  • A pnKoch objektumnak küldött repaint() üzenet/metódushívás meghívja a felüldefiniált paintComponent() metódust.

A szakasz négy darab harmad akkora szakaszra osztását a megfelelően paraméterezett rekurzív metódushívások oldják meg az alábbi lépéseket követve:

Koch-görbe rajzolásának fázisai

A rekurzív rajzolást a koch() metódus végzi el, ahol a fraktál szabályának megfelelően szakaszharmadolás és a szükséges pontok koordinátáinak (szakaszok végpontjai) kiszámítása történik:

A Koch-görbének van néhány érdekes tulajdonsága:

  • kerülete minden rekurzív lépésben minden határon túl növekszik, azaz a végtelenhez tart,
  • területe véges, hiszen minden rekurzív lépésben belefér a háromszög köré írható körbe,
  • dimenziója tört, ~ 1,261859.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 9-12. óra: Metódusok, rekurzió alkalomra építő 29-36. Grafikus felhasználói felület alkalomhoz kötődik.

A képernyőről a videó a FlashBack Express programmal, a videóból az animált gif az aconvert.com weboldalon készült.