Pátá zápočtová práce z předmětu Programování v Matlabu
Martin Dlask
Implementace hry „Had“ (angl. Snake) pomocí GUI 1.1 Úvod Ze dvou nabízených témat k naprogramování v Matlabu na poslední úkol jsem si zvolil vytvoření aplikace pomocí GUI. Jelikož jsem chtěl vytvořit nějaký program, který by obsahoval jak formulář s menu a dostatečnými vstupy a interakcí pro uživatele, tak algoritmus, který není příliš elementární, padl výběr na implementaci klasické hry had. I když se zdá na první pohled implementace hry v interpretačním jazyku, který je spíše určen pro matematické výpočty a práci s maticemi, bizarně, využívá tento program přesto několik matematických funkcí a výpočtů s číselnými tabulkami.
1.2 Představení hry, pravidla Herní prostředí sestává ze čtverce, ve kterém se pohybuje had, který se může pohybovat ve čtyřech směrech (doprava, doleva, nahoru, dolů), a tudíž se může pohybovat po celém herním poli. Na tomto čtverci se také nachází potrava. Herní pole má takzvané „propustné zdi“ v situaci, kdy by chtěl had „vyjet“ z tohoto herního pole a překročit okraj, se objeví na druhém konci herního pole. Podle toho, na který okraj had vjel, se postupně transportuje na druhý okraj. Hra probíhá v krátkých časových krocích. V každé iteraci se může uživatel rozhodnout, jestli změní směr pohybu hada, nebo jestli chce hru pozastavit. Had se pohne posledním určeným směrem a v případě, že sebral potravu (pixel jeho hlavy překryje pixel potravy) zvětší svoji délku o jedna. Jak přesně probíhá přesun hada propustnými zdmi, je vidět z následujícího schématu: [1,1]
[1,R]
[R,1]
px
…
x-ová souřadnice hada (svislá)
py
…
y-ová souřadnice hada (vodorovná)
sh
…
směr hada
[R,R]
sh = 1
…
konstantní souřadnice y, snižující se souřadnice x
↑
sh = 2
…
konstantní souřadnice y, zvyšující se souřadnice x
↓
sh = 3
…
konstantní souřadnice x, snižující se souřadnice y
←
sh = 4
…
konstantní souřadnice x, zvyšující se souřadnice y
→
1
Pátá zápočtová práce z předmětu Programování v Matlabu
Martin Dlask
{
}
sh = 1
nová pozice je [
]
{
}
sh = 2
nová pozice je [
]
{
}
sh = 3
nová pozice je [
]
{
}
sh = 4
nová pozice je [
]
Kudy se had pohybuje, shrnuje následující pravidlo. Směr každé části hada se pohybuje v časovém okamžiku v takovém směru, jímž se pohybovala jeho hlava, když se nacházela právě ve stejné pozici. Hra skončí v okamžiku, kdy had narazí sám do sebe. To může nastat v případě, že uživatel svou nepozorností dopustil, aby had sám do sebe narazil nebo v případě, že počet pixelů hada již dosáhl délky R2.
1.3 Krátký historický exkurz První verze této hry byla představena v roce 1972 pro tehdejší počítače a dále se objevovala na mnoha platformách až do současné doby. Svojí největší popularity dosáhl Had v roce 1998, kdy byl představen jako mobilní telefonní hra na telefon Nokia 3310. Od roku 2011 je tajně implementován jako tzv. „Easter Egg“ na serverech Youtube a službě Gmail.
1.4 Funkce a interface programu, možnosti pro uživatele Na úrovni kódu se dá na začátku nastavit, jaký bude rozměr herního pole (a tím také velikost jednoho barevného políčka), barva pozadí, barva hada a barva potravy. Po spuštění skriptu se objeví okno, ve kterém je jak herní pole, ve které se nachází had ve své výchozí pozici, tak i potrava. Na pravé straně se nachází ovládací panel se směrovými tlačítky pro pohyb doprava, doleva, nahoru a dolů a také tlačítko pro nastartování nebo pozastavení hry. V horním menu je možné hru restartovat (načíst novou hru) a případně zobrazit kredity pod položkou About. Hra se spouští oranžovým tlačítkem Start. V okamžiku stisknutí má had délku 3 a je ve své výchozí pozici a začne se pohybovat směrem doprava. Směr pohybu lze ještě před nastartováním hry změnit pomocí jednoho ze čtyř směrových tlačítek. V případě, že had sebere potravu, se zvětší o jedna a také se přičte bod v tabulce vpravo dole. Oranžové tlačítko je univerzální – hra lze v jakémkoliv okamžiku přerušit tlačítkem Pauza, které vzniklo z tlačítka Start. Po jeho stisknutí se přemění na tlačítko Pokračovat pro pokračování hry z pozice, ve které byla přerušena.
2
Pátá zápočtová práce z předmětu Programování v Matlabu
Martin Dlask
Rychlost pohybu hada se zvyšuje se zvyšujícím se počtem bodů lineárně. Je tedy obtížnější sebrat každou další potravu oproti předchozí. Hra skončí v případě, že ji sám uživatel skončí buď zavřením celého okna například kliknutím na ikonu křížku v pravém horním rohu, nebo restartováním hry kliknutím na Nova hra v menu, případně tak, že had narazí sám do sebe.
Snímek herního okna při 15-tém levelu. Hra je v tento okamžik pozastavena a tudíž je místo tlačítka Pauza tlačítko Pokracovat, kterým se dá do rozehrané hry opět vrátit.
1.5 Program function varargout = had(varargin) % Begin initialization code - DO NOT EDIT gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... 'gui_Singleton', gui_Singleton, ... 'gui_OpeningFcn', @had_OpeningFcn, ... 'gui_OutputFcn', @had_OutputFcn, ... 'gui_LayoutFcn', [] , ... 'gui_Callback', []); if nargin && ischar(varargin{1}) gui_State.gui_Callback = str2func(varargin{1}); end if nargout [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
3
Pátá zápočtová práce z předmětu Programování v Matlabu
Martin Dlask
else gui_mainfcn(gui_State, varargin{:}); end % End initialization code - DO NOT EDIT % --- Executes just before had is made visible. function had_OpeningFcn(hObject, eventdata, handles, varargin) % This function has no output args, see OutputFcn. % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % varargin command line arguments to had (see VARARGIN) % Choose default command line output for had handles.output = hObject; % Update handles structure guidata(hObject, handles); % UIWAIT makes had wait for user response (see UIRESUME) % uiwait(handles.figure1); % --- Outputs from this function are returned to the command line. function varargout = had_OutputFcn(hObject, eventdata, handles) % varargout cell array for returning output args (see VARARGOUT); % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Get default command line output from handles structure varargout{1} = handles.output;
global global global global global global global global
barvahada; barvapozadi; smerhada; probihahra; pauza; rozmer; delkahada; potrava;
% globální proměnné potřebné v průběhu hry
rozmer = 50; probihahra = 0; pauza = 1; smerhada = 6; barvahada = 255; barvapozadi = 99; delkahada = 3; potrava(1,1) = randi([1,rozmer]); potrava(1,2) = randi([1,rozmer]); SouradniceHada(1,1) SouradniceHada(1,2) SouradniceHada(2,1) SouradniceHada(2,2) SouradniceHada(3,1)
= = = = =
% generování pozice potravy
25; 25; 25; 24; 25;
% defaultní pozice hada
4
Pátá zápočtová práce z předmětu Programování v Matlabu
Martin Dlask
SouradniceHada(3,2) = 23; CervenaT = zeros(rozmer,rozmer); for i=1:delkahada CervenaT(SouradniceHada(i,1),SouradniceHada(i,2)) = barvahada; end % barevné plochy pro vykreslení funkcí imshow() ZelenaT = ones(rozmer,rozmer)*barvapozadi; ModraT = zeros(rozmer,rozmer); CervenaT(potrava(1,1),potrava(1,2)) = 255; ZelenaT(potrava(1,1),potrava(1,2)) = 255; ModraT(potrava(1,1),potrava(1,2)) = 0; Obrazek=cat(3,CervenaT,ZelenaT,ModraT); imshow(uint8(Obrazek)); set(handles.pushbutton5,'String','Start'); set(handles.text2,'String',num2str(delkahada-3)); while 1 if probihahra==1
% pokud probíhá hra, tj. není pozastavena
switch smerhada case 2 % směr dolů pomocna = zeros(delkahada,2); pomocna(2:delkahada,:) = SouradniceHada(1:delkahada-1,:); pomocna(1,2) = pomocna(2,2); if pomocna(2,1)+1 > rozmer pomocna(1,1) = 1; else pomocna(1,1) = pomocna(2,1)+1; end case 4 % směr doleva pomocna = zeros(delkahada,2); pomocna(2:delkahada,:) = SouradniceHada(1:delkahada-1,:); pomocna(1,1) = pomocna(2,1); if pomocna(2,2)-1<1 pomocna(1,2) = rozmer; else pomocna(1,2) = pomocna(2,2)-1; end case 6 % směr doprava pomocna = zeros(delkahada,2); pomocna(2:delkahada,:) = SouradniceHada(1:delkahada-1,:); pomocna(1,1) = pomocna(2,1); if pomocna(2,2)+1 > rozmer pomocna(1,2) = 1; else pomocna(1,2) = pomocna(2,2)+1; end
5
Pátá zápočtová práce z předmětu Programování v Matlabu
Martin Dlask
case 8 % směr nahoru pomocna = zeros(delkahada,2); pomocna(2:delkahada,:) = SouradniceHada(1:delkahada-1,:); pomocna(1,2) = pomocna(2,2); if pomocna(2,1)-1 < 1 pomocna(1,1) = rozmer; else pomocna(1,1) = pomocna(2,1)-1; end end % v případě, že had narazil sám do sebe if (CervenaT(pomocna(1,1),pomocna(1,2))== barvahada) && (ModraT(pomocna(1,1),pomocna(1,2))== 0) && (ZelenaT(pomocna(1,1),pomocna(1,2))== 0) errordlg('Konec hry.'); probihahra = 0; break; end SouradniceHada = pomocna; CervenaT = zeros(rozmer,rozmer); ZelenaT = ones(rozmer,rozmer)*barvapozadi; ModraT = zeros(rozmer,rozmer); CervenaT(potrava(1,1),potrava(1,2)) = 255; ZelenaT(potrava(1,1),potrava(1,2)) = 255; ModraT(potrava(1,1),potrava(1,2)) = 0; for i=1:delkahada CervenaT(SouradniceHada(i,1),SouradniceHada(i,2)) = barvahada; ModraT(SouradniceHada(i,1),SouradniceHada(i,2)) = 0; ZelenaT(SouradniceHada(i,1),SouradniceHada(i,2)) = 0; end Obrazek=cat(3,CervenaT,ZelenaT,ModraT); imshow(uint8(Obrazek)); if (SouradniceHada(1,1)==potrava(1,1)) && % došlo k sežrání potravy (SouradniceHada(1,2)==potrava(1,2)) delkahada = delkahada+1; potrava(1,1) = randi([1,rozmer]); potrava(1,2) = randi([1,rozmer]); set(handles.text2,'String',num2str(delkahada-3)); end end pause(abs(delkahada-3-64)/64*0.1); end function pushbutton1_Callback(hObject, eventdata, handles) global smerhada; smerhada = 8;
6
Pátá zápočtová práce z předmětu Programování v Matlabu function pushbutton2_Callback(hObject, eventdata, handles) global smerhada; smerhada = 4; function pushbutton3_Callback(hObject, eventdata, handles) global smerhada; smerhada = 6; function pushbutton4_Callback(hObject, eventdata, handles) global smerhada; smerhada = 2; function pushbutton5_Callback(hObject, eventdata, handles) global probihahra; global pauza; if probihahra == 1 pauza = 1; probihahra = 0; set(handles.pushbutton5,'String','Pokracovat'); else pauza = 0; probihahra = 1; set(handles.pushbutton5,'String','Pauza'); end function KeyPress(Source, EventData) disp(EventData) function konec_Callback(hObject, eventdata, handles) close(had); function About_Callback(hObject, eventdata, handles) msgbox('Hra Had, Martin Dlask © 2012','About','help')
7
Martin Dlask
Pátá zápočtová práce z předmětu Programování v Matlabu
1.6 Editor GUI Na tomto obrázku je vidět, jak vypadá návrh okna programu v módu GUIDE.
1.7 Výstupy na obrazovku Na začátku (nebo v případě Nové hry) vypadá okno následujícím způsobem:
8
Martin Dlask
Pátá zápočtová práce z předmětu Programování v Matlabu
Po kliknutí na tlačítko Start vypadá následovně:
V případě, že je hra pozastavena, je místo tlačítka Pauza tlačítko Pokračovat:
9
Martin Dlask
Pátá zápočtová práce z předmětu Programování v Matlabu
Martin Dlask
Hra skončí například v případě zobrazeném na dalším obrázku. Potom je možné program zavřít, nebo vyvolat Novou hru příkazem v menu. Dokud tlačítko uživatel nestiskne, nová hra se nespustí a může si prohlédnout, proč mu hra skončila.
Možné je také si zobrazit údaje o autorovi :
10
Pátá zápočtová práce z předmětu Programování v Matlabu
Martin Dlask
1.7 Závěr Původní klasická hra Snake neměla tolik vylepšení, která skýtají její „novější“ verze. Přidány byly postupně zejména možnost „průchozích zdí“ a také postupné zrychlování hry při vyšších levelech a možnost pauzy. Matlab sice není úplně primárně program na vývoj herních aplikací, nicméně jednodušší grafické výstupy se v něm dají zobrazit poměrně jednoduchým způsobem a také vytvoření uživatelského prostředí spolu s nastavováním funkcí je docela intuitivní.
11