Code covered by the BSD License  

Highlights from
Tetris for Dummies

image thumbnail
from Tetris for Dummies by Husam Aldahiyat
Tetris with easy code layout and extensive comments.

play2(varargin)
function play2(varargin)
% MATLAB Tetris
% To launch, simply type 'play2' in your command window.
% Alternatively, you can choose run from this editor window, or press 'F5'.
% 
% To start the game, place the cursor in the 'Start' edit box, and press Enter
% on your keyboard. Controls are as follows:
% 
% [Q/q]: Left
% [E/e]: Right
% [S/s]: Down
% [W/w]: Flip
% [P/p]: Pause/Unpause
% 
% Setting the Gravity option to 'On' will make all blocks falls downwards when
% a line if formed (regardless of the line's location). Thus after each line
% formed all the blocks will become next to each other (i.e. no blocks
% floating in mid air). This allows you to get big combos and big scores.
% 
% The controls can be edited in the code, as can most variables
% and aesthetic properties.
% 
% Code written on MATLAB 7.4 (will not work on earlier versions).
% 
% Husam Aldahiyat, Jordan, 2008.
%

%main figure layout. the figure is currently not visible.
hfig=figure('visible','off','Menubar','none','name','Tetris','Numbertitle','off',...
    'color',[.5 .2 .8],'units','normalized','resize','off','position',[.3 .05 .3 .9*.9]);

%The start edit box.
%In order to maintain the figure as always active (as opposed to
%going to the command window whenever a button is pressed), we make this edit box.
%As soon as you press enter, we go to its commanding function where the box becomes so
%small it is invisible to the human eye. Now when you type, the letters are written
%in the edit box and not in the command window, thus you can move the pieces via
%keyboard, and everything you type is in this edit box (but you can't see it).
h2=uicontrol('style','edit','string','Start','callback',@goon,'units','normalized',...
    'position',[.5 .05 .2 .05],'backgroundcolor',[.7 .8 .9]);

%the main axes that covers most of the figure window. our workground.
%change the x and y colours and use 'grid on' to view it in detail.
axes('Units','normalized','Position', [.05 .06 .9 .93],'Visible','on',...   	
'DrawMode','fast','NextPlot','replace','XLim',[1,5],'YLim',[1,15*.9]...
,'color',[.5 .2 .8],'xcolor',[.5 .2 .8],'ycolor',[.5 .2 .8]);

%dump text box for later use, will determine whether or not to continue the game
%(game over). when you lose the game, this text's string will change, signaling
%the end of creating additional pieces. it will always be invisibe.
h3=uicontrol('style','text','visible','off');

%the gravity radio buttons
hr=uibuttongroup('Units','normalized',...
'Position',[.1 .05 .25 .025],'SelectionChangeFcn',@grv);
hb1=uicontrol('Style','Radiobutton','String','On','Units','normalized',...
'Position',[0 0 .5 1],'Parent',hr,'backgroundcolor',[.7 .8 .9],'foregroundcolor',[0 0 0]);
hb2=uicontrol('Style','Radiobutton','String','Off','Units','normalized',...
'Position',[0.5 0 .5 1],'Parent',hr,'backgroundcolor',[.7 .8 .9],'foregroundcolor',[0 0 0]);
set(hr,'SelectedObject',hb1)

    function agvv=grv(varargin)     %function that checks which radio button is ticked.
        rbr=get(hr,'SelectedObject');
        switch rbr        
            case hb1            
                agvv='On';		
            case hb2				
                agvv='Off';
        end
    end

%initialization function. it clears the axes and draws the rightside texts
    function init(varargin)    
        cla
        text('position',[4.35 12.8],'string','NEXT','color','w');
        set(line([4.1,4.1,4.9,4.9],[7.3,9,9,7.3]+4),'Color',[0,0,0]);    
        set(line([4.1,4.9],[7.3,7.3]+4),'Color',[0,0,0]);    
        set(line([1,1,4,4],[2,13,13,2]),'Color',[0,0,0]);    
        set(line([1,4],[2,2]),'Color',[0,0,0]);   
        set(h2,'string','Start')    %the edit box retains its original string value          
        set(h3,'string','text')     %dummy text box is set to default value
        text('position',[4.1 10],'string','Complete Lines','color','w');
        text('position',[4.45 7.5],'string','Level','color','w');
        text('position',[4.4 2.5],'string','Combo','color','w');
        text('position',[4.45 5],'string','Score','color','w');
        text('position',[1.5 1.5],'string','Gravity','color','w');       
    end
init   
%more initialization
%these are the scoring values. they can be used in the nested functions and
%changed according to play
cls=text('position',[4.6 9.55],'string','0','color','w'); %completed lines
lvl=text('position',[4.6 7.05],'string',num2str(1),'color','w'); %level               
scr=text('position',[4.5 4.55],'string',num2str(0),'color','w'); %score
text('position',[4.6 1.95],'string',num2str(0),'color','w'); %combos
%menus
m1=uimenu('Label','Game');
m2=uimenu('Label','More');
uimenu(m1,'Label','Reset','Callback',@goon2);
mmm=uimenu(m1,'Label','Turn Off Sound','Callback',@goon4);
uimenu(m1,'Label','High Score','Callback',@goonh);
uimenu(m2,'Label','Controls','Callback',@goon5);
uimenu(m2,'Label','About','Callback',@goon3);

%after everything is set in place, make the figure appear.
set(hfig,'Visible','on');  

    function goon4(varargin)       %sound menu function, switches 'on'/'off' string,
        switch(get(mmm,'label'))   %for user interface and future reference.
            case 'Turn Off Sound'
                set(mmm,'label','Turn On Sound')
            case 'Turn On Sound'
                set(mmm,'label','Turn Off Sound')            
        end
    end

    function goon3(varargin)    %cheap plug function. just a new figure and some texts.
        gg11=figure('Menubar','none','Numbertitle','off','name','About','units','normalized','resize','off','position',[.3 .7 .25 .15]);
        text('position',[.002 .9],'string','MATLAB Tetris by Husam Aldahiyat,') 
        text('position',[-.01 .65],'string','Mechatronics Eng. student, University of Jordan.')
        text('position',[.05 .4],'string','numandina@gmail.com')
        text('position',[.002 .15],'string','Written on Matlab 7.4 (R2007a).')
        %set same colour for everything so you would not see any axes lines or numbers
        set(gca,'color',get(gg11,'color'),'xcolor',get(gg11,'color'),'ycolor',get(gg11,'color'))        
    end

    function goon5(varargin) %more texts and a new figure
        gg2=figure('Menubar','none','Numbertitle','off','name','Controls','units','normalized','resize','off','position',[.2 .6 .45 .25]);                
        text('position',[-.1 .9],'string','                Use the Keyboard to move the pieces','fontname','courier')
        text('position',[-.1 .65],'string','[Q/q]: Left      [E/e]: Right      [S/s]: Down      [W/w]: Flip','fontname','courier')
        text('position',[-.1 .15],'string','You can easily alter these controls in the code itself.','fontname','courier')
        text('position',[-.1 .4],'string','[P/p]: Pause the Game','fontname','courier')
        set(gca,'color',get(gg2,'color'),'xcolor',get(gg2,'color'),'ycolor',get(gg2,'color'))
    end

    function goon2(varargin) %the reset function     
        init                            
        cls=text('position',[4.6 9.55],'string','0','color','w');                        
        scr=text('position',[4.5 4.55],'string',num2str(0),'color','w');
        lvl=text('position',[4.6 7.05],'string',num2str(1),'color','w');
        text('position',[4.6 1.95],'string',num2str(0),'color','w');                                
        set(h2,'position',[.5 .05 .2 .05]) %give the edit function its original size
        set(hr,'visible','on')             %make the radio buttons reappear
        set(hr,'SelectedObject',hb1)
    end

    function goonh(varargin) %the high score function                    
        gg2=figure('Menubar','none','Numbertitle','off','name','High Score','units','normalized','resize','off','position',[.2 .6 .45 .25]);                
        text('position',[-.1 .85],'string','High Score:','fontname','courier','fontweight','bold','fontsize',44)       
        if ~exist('hsc.mat','file') %if this is the first time and there is no high score file   
            highs=0; %set high score to 0
            save hsc highs %and save it as zero for future reference 
        end        
        load hsc highs %load the high score highs from the file hsc
        text('position',[-.1 .25],'string',num2str(highs),'fontname','courier','fontweight','bold','fontsize',44)        
        set(gca,'color',get(gg2,'color'),'xcolor',get(gg2,'color'),'ycolor',get(gg2,'color'))
    end

    function goon(varargin)           %main function. starts the game.
        set(hr,'visible','off')       %sets radio buttons invisible.
        
        
        %this is our workground but in matrix form, consisting of ones and zeros.
        %the figure box in which the pieces are moving in spans x=1:4 and y=2:13.
        %given length ratios, each single square block will take 0.25 horizontally
        %and 0.5 vertically. this means that all in all, we have 21 by 12 possible
        %positions for each square block.
        %everything that changes in the matrix will see its equivalent change in the
        %figure window, and vice versa.
        map=zeros(21,12);             
              
        set(h2,'position',[.1 .1 .00000001 .0000001]) %make edit box too small to see
        j=0;                                          %counter to signal first time start
        xstopx=0;                     %mostly insignificant, used for next while loop
        text('position',[1.575 1.1],'string',grv,'color','w'); %gravity on/off text
              
        while ~xstopx                %main loop 1: continually creating new pieces to drop
            timm=.34/(str2double(get(lvl,'string'))); %time increment, depends on level reached
            
            %at the beginning of the loop, we check for any completed lines before
            %making a new piece
            [rn,yn]=chk(map);    %chk is the function that checks for complete lines
            %the yn variable returns 1 if a complete line exists, and 0 otherwise.
            %rn is the row number in which this happens. since the map matrix and the
            %figure window are mirrors to each other, a complete line in map equals
            %one in our figure window.
            
            if yn      %the proceeding occurs if indeed there is a complete line due
                       %to the previous piece landing.
                       %you can return here after reading the rest of the code first.
                pause(timm)    %extra pause so you could see the line before it disappears             
                
                for howz=1:6   %this loop makes the line flashes before disappearing  
                    %we flash by changing the line's colour from red to yellow and
                    %back, three times each.                    
                    switch mod(howz,2)    %alternates between 1 and 2, red and yellow
                        case 1                       
                            cr=[1 0 0];   %[r g b], [1 0 0] means red
                        case 0                       
                            cr='y';
                    end
                    %we color each of the 12 blocks in the row with the variable 'cr'
                    for k1=1:12                  %12 blocks in a row
                        for k2=rn                %same row, the complete line
                            %the following command fills the 'k1'th block of the row
                            %with the 'cr' colour.                            
                            patch([(k1-1)/4+1,(k1-1)/4+1,(k1-1)/4+1+.25,(k1-1)/4+1+.25],[(21-k2)/2+2,(21-k2)/2+2+.5,(21-k2)/2+2+.5,(21-k2)/2+2],cr)                          
                        end
                    end
                    pause(timm/2) %after twelve 'colouring in' operations, the whole line
                    %is coloured, so we pause for a while and display before using the next colour.
                end %end of flashing
                
                sound2(4) %make a disappearing line sound.
                
                %next comes updating of score values after the completion of a line
                %if this is not the very first line completed (i.e. cls~=0)
                if (str2double(get(cls,'string'))) 
                    prevlvl=newlvlno; %get previous value of level
                end
                newv=(num2str(str2double(get(cls,'string'))+1)); %add 1 to number of lines
                newlvlno=floor(str2double(newv)/10)+1; %update and get new value of level
                %if new level is different than old level, this means you've reached
                %a new level.
                if (str2double(get(cls,'string'))) && prevlvl~=newlvlno                         
                    musicc(2) %cue celebration music.      
                end
                %new score value, depends on previous score, level and combo.
                nrz=num2str(str2double(get(scr,'string'))+50*(floor((str2double(newv)-1)/10)+1)+3^(combo+1)*(combo));
                init %clear axes and update scores
                text('position',[1.575 1.1],'string',grv,'color','w');             
                lvl=text('position',[4.6 7.05],'string',num2str(newlvlno),'color','w');
                scr=text('position',[4.5 4.55],'string',(nrz),'color','w');                                                       
                cls=text('position',[4.6 9.55],'string',newv,'color','w');                
                h=drawshape1(chz(2),hic(2)); %draws the shape in the 'NEXT' box, depending
                %on chz and hic, introduced later.
                map(2:rn,1:12)=map(1:rn-1,1:12); %line disappears higher blocks take its place
                
                %since the axes got cleared, we need to redraw every block, based on
                %the variable map, where every 1 means there's a block in the
                %equivalent axes window.
                for k1=1:12
                    for k2=1:21
                        if map(k2,k1)
                            patch([(k1-1)/4+1,(k1-1)/4+1,(k1-1)/4+1+.25,(k1-1)/4+1+.25],[(21-k2)/2+2,(21-k2)/2+2+.5,(21-k2)/2+2+.5,(21-k2)/2+2],[1 1 1])
                        end
                    end
                end
                combo=combo+1;
                text('position',[4.6 1.95],'string',num2str(combo),'color','w');

                if strcmp(grv,'On') %if gravity is turned on, we still have some work to do
                    mapb=map;  %get map
                    map=hrav(map); %get new map, adjusted after gravity effect
                    
                    %if map before and after is the same, then gravity changed
                    %nothing, and we continue out of the loop (cha is almost 0).
                    cha=sum(sum(abs(map-mapb))); 
                    if cha>1 %else if gravity changed the map                        
                        jov=text('position',[1.25,7.5],'String','Gravity','color','w','fontsize',40) ;       
                        pause(.25) %these three commands make the quick 'Gravity' flash appear
                        delete(jov)
                        init                  %clear axes and redraw new blocks
                        text('position',[1.575 1.1],'string',grv,'color','w');                       
                        lvl=text('position',[4.6 7.05],'string',num2str(newlvlno),'color','w');
                        scr=text('position',[4.55 4.55],'string',(nrz),'color','w');                                                        
                        cls=text('position',[4.6 9.55],'string',newv,'color','w');
                        text('position',[4.6 1.95],'string',num2str(combo),'color','w');                
                        h=drawshape1(chz(2),hic(2));
                        for k1=1:12
                            for k2=1:21
                                if map(k2,k1)
                                    patch([(k1-1)/4+1,(k1-1)/4+1,(k1-1)/4+1+.25,(k1-1)/4+1+.25],[(21-k2)/2+2,(21-k2)/2+2+.5,(21-k2)/2+2+.5,(21-k2)/2+2],[1 1 1])
                                end
                            end
                        end
                        sound2(4)   %make another line falling sound                                     
                    end
                else
                end
                %this returns us to the start of the loop again to check for
                %additional lines before creating any new pieces.
                continue
            end
            combo=0;
            for bnm=5:8                
                if map(2,bnm) %if the top of map isn't free, it means game over.
                    %this is checked before creating new pieces.
                    set(h3,'string','nomore') %this signals the game over status
                end
            end
            if strcmp(get(h3,'string'),'nomore') %if game is indeed over
                %make text and end game music
                text('color',[1 0 0],'string','GAME OVER','position',[1.1 8.5],'fontsize',40)
                pause(.0001)
                musicc(1)                %cue sad music                  
                if ~exist('hsc.mat','file') %if this is the first time and there is no high score file   
                    highs=0; %set high score to 0
                    save hsc highs %and save it as zero for future reference 
                end                
                load hsc highs %loads high score from file hsc                
                if str2double(get(scr,'string')) > highs %if current score is higher than the high score,                    
                    highs=str2double(get(scr,'string'));
                    save hsc highs %save current score as high score
                end
                return %get out of the function
            end
            j=j+1;   %this variable's sole purpose is to show whether it's the first time
                     %a piece is created or not (if j==1). after the very first loop 
                     %it will have completed its purpose.       
                     
            %if it's not the very of the game, delete the piece's previous place.
            %you see the way this works is: a piece is plotted using the drawing
            %function, and each of its 4 blocks are given a handle. in the next loop
            %we draw the same piece again but one block lower, and delete the previous
            %handles. thus it looks like the piece dropped down.
            %for the very first time there is nothing previous to delete, and then
            %if a piece reaches the ground or something below it, we get
            %out of the loop and make a new piece (and nothing gets deleted fot another
            %loop). this will be seen later in the code.
            if j~=1 
                for kkk=1:4
                    delete(h(kkk)) %delete the 4 blocks in NEXT box
                end
            end
            
            %this is how we randomly choose pieces and their starting orientations.
            %chz is the piece, from 1 to 7, and hic is the flip side, from 1 to 4.
            if j==1  %for the very first time, we randomly create two values each
                     %one for the current piece, and one for the one after it (NEXT)
                chz=[floor(rand*7)+1;floor(rand*7)+1];               
                hic=[floor(rand*4)+1;floor(rand*4)+1];    
            else %after that, the first value is the second one, and new second value is made
                chz(1)=chz(2);                
                chz(2)=floor(rand*7)+1;                    
                hic(1)=hic(2);                
                hic(2)=floor(rand*4)+1;
            end
            h=drawshape1(chz(2),hic(2)); %this function draws the second values in the NEXT box
            d=1; %this variable shows how far down a piece has fallen.
            r=0; %this variable shows how much right or left a piece a piece moved.
            
            while 1 %main loop 2: each piece falling down until it settles.
                if d==1 %if this is the piece's first occurrence, there is no previous
                    prev=666; %place, so 'prev' is set to signal this.
                end               
                
                if d~=1 %if this isn't the piece's first occurrence,
                    %first we see which keyboard key is pressed (if any)
                    %if you press q/Q (left)
                    
                    if strcmp(get(hfig,'currentcharacter'),'q') || strcmp(get(hfig,'currentcharacter'),'Q')                       
                        
                        %condr1 and condr2 are the two conditions that prevent a piece
                        %from going left. the variable prev contains the coordinates
                        %of the piece's previous position. row 1 shows the y coordinates,
                        %and row 2 the x coordinates. prev is obtained from drawshape2.
                        condr1=0; %initially, left movement is allowed unless proven otherwise.
                        if prev(2,ghj2(1))==1 %ghj2 is the minimum x coordinate index. if the minimum
                            condr1=1;         %x coordinate is 1 (i.e. adjoined to the leftmost wall), we cannot
                        end                   %move the piece to the left. prev(2,ghj2(1))== min(prev(2,:)).
                        condr2=0;             %second condition                                           
                        %in the next loop we check if there is a block to the left of every minimum x coordinate.
                        %(map(prev(1,ghj2(klk)),prev(2,ghj2(klk))-1)==map(y(x_klk),x_klk-1),
                        %where x_klk is the 'klk'th minimum x coordinate, and x_klk-1 means the block to its left.
                        %map(prev(1,ghj2(klk))+1,prev(2,ghj2(klk))-1))==map(y(x_klk)+1,x_klk-1)
                        %checks one block to its left and one block below.
                        for klk=1:numel(ghj2)                                               
                            if ~condr1 && (map(prev(1,ghj2(klk)),prev(2,ghj2(klk))-1) || map(prev(1,ghj2(klk))+1,prev(2,ghj2(klk))-1))
                                condr2=1; %once instance is sufficient for setting this condition on.
                            end
                        end
                        vvv=find(yyy==max(yyy));
                        vvi=min(xxx(vvv));                     
                        %another condition. try to figure it out.
                        if ~condr1 && (map(max(yyy),(vvi)-1) || map((max(yyy))+1,vvi-1))                        
                            condr2=1;
                        end
                        %the following are special cases.
                        switch chz(1)
                            case 3 %inverted L block
                                switch hic(1)
                                    case 4   %inverted L orientation
                                        if map(min(prev(1,:))+1,min(prev(2,:))) || map(min(prev(1,:)),min(prev(2,:)))
                                            condr2=1;
                                        end
                                end
                            case 4 %L block
                                switch hic(1)
                                    case 4 %inverted upside down L orientation
                                        if map(max(prev(1,:)),min(prev(2,:))) || map(max(prev(1,:))+1,min(prev(2,:)))
                                            condr2=1;
                                        end
                                end
                        end
                        %if all prevention condition are 0 and you press q/Q
                        if ~condr1 && ~condr2
                            %we set r to its new value so we would move the piece's
                            %four blocks one place to the left later
                            r=r-1; 
                            sound2(2) %and make a movement sound
                        else
                            sound2(6) %else, make error sound
                        end
                        
                    elseif strcmp(get(hfig,'currentcharacter'),'e') || strcmp(get(hfig,'currentcharacter'),'E')
                        %next are the conditions for moving right
                        condl1=0;
                        if max(xxx)==12 %if the piece is next to the rightmost wall
                            condl1=1;
                        end
                        condl2=0;
                        ghjk=find(xxx==max(xxx));                                     
                        for klk=1:numel(ghjk) %just like before
                            if ~condl1 && (map(prev(1,ghjk(klk)),prev(2,ghjk(klk))+1) || map(prev(1,ghjk(klk))+1,prev(2,ghjk(klk))+1))                        
                                condl2=1;
                            end
                        end
                        vvv=find(yyy==max(yyy));
                        vvi=max(xxx(vvv));                     
                        if ~condl1 && (map(max(yyy),(vvi)+1) || map((max(yyy))+1,vvi+1))                        
                            condl2=1;
                        end
                        switch chz(1) %special cases
                            case 3    %inverted L block
                                switch hic(1)
                                    case 2 %upside down L orientation
                                        if map(max(prev(1,:)),max(prev(2,:))) || map(max(prev(1,:))+1,max(prev(2,:)))
                                            condl2=1;
                                        end
                                end
                            case 4    %L block
                                switch hic(1)
                                    case 2 %L orientation
                                        if map(min(prev(1,:)),max(prev(2,:))) || map(min(prev(1,:))+1,max(prev(2,:)))
                                            condl2=1;
                                        end
                                end
                        end
                        if ~condl1 && ~condl2
                            r=r+1;
                            sound2(1)
                        else
                            sound2(6)
                        end
                    elseif strcmp(get(hfig,'currentcharacter'),'s') || strcmp(get(hfig,'currentcharacter'),'S')
                        %if you press s (down), the time increment become very small and the block appears to
                        %be going to the ground very fast. you can set this number to 0 to see the block
                        %immediately reach the ground. this happens because the display only updates after a pause.  
                        timm=1e-10;               
                    elseif strcmp(get(hfig,'currentcharacter'),'p') || strcmp(get(hfig,'currentcharacter'),'P')   
                        %if you try to pause, the code changes the current letter input and puts you in an infinite
                        %loop. the only to get out of the loop is by pressing p again.
                        set(hfig,'currentcharacter','l')                                                 
                        sound2(1)   %pause sound, same as moving right sound
                        %a text box appears while paused
                        hhj=uicontrol('style','text','units','normalized','position',[.2 .74 .4 .05],'string','Game Paused','backgroundcolor',[0 0 0],'foregroundcolor','w','fontsize',16);                     
                        while ~(strcmp(get(hfig,'currentcharacter'),'p') || strcmp(get(hfig,'currentcharacter'),'P'))                                                                                
                            pause(.2) %infinite loop until you press p
                        end
                        delete(hhj) %when out of the loop, delete the text box
                        sound2(1) %and make another pause sound
                    elseif strcmp(get(hfig,'currentcharacter'),'w') || strcmp(get(hfig,'currentcharacter'),'W')                                          
                        %pressing w/W will make the piece 'flip'. unlike moving left/right, the conditions                        
                        %for allowing a piece to flip will only be checked after pressing the key.
                        pass=1;      %initially, you can flip unless proven otherwise               
                        %next, we take each individual case and test the allowability to flip it one time
                        switch chz(1)                        
                            case 1           %4 block line                                                
                                switch hic(1)                                
                                    case {1,3} %horizontal
                                        if yyy(1)>18 
                                            pass=0;  %if it's near the ground, you can't flip it
                                        end
                                        if pass &&(map(yyy(1)-1,max(xxx)-1) || map(yyy(1)+3,max(xxx)-1) || map(yyy(1)+1,max(xxx)-1) || map(yyy(1)+2,max(xxx)-1))
                                            pass=0; %if there are blocks below or above it, don't flip
                                        end
                                    case {2,4} %vertical
                                        if xxx(1)==12 || xxx(1)<3
                                            pass=0; %if it's stuck to the right wall, or near the left wall
                                        end
                                        if pass &&(map(max(yyy)-1,xxx(1)-2) || map(max(yyy)-1,xxx(1)-1) || map(max(yyy)-1,xxx(1)+1))
                                            pass=0; %if there are blocks to its left or right
                                        end
                                end
                                %case 2 is the square piece, and it can't be flipped!
                            case 3 %and so on...
                                switch hic(1) 
                                    case 1
                                        if (max(yyy))>19
                                            pass=0;
                                        end
                                        if pass && (map(min(yyy),min(xxx)+1) || (map(min(yyy),min(xxx)+2)) || (map(min(yyy)+2,min(xxx)+1)) || (map(min(yyy)+3,min(xxx)+1)) || (map(min(yyy),min(xxx)+2)))
                                            pass=0;
                                        end
                                    case 2
                                        if min(xxx)==1
                                            pass=0;
                                        end
                                        if pass && (map(min(yyy)+1,min(xxx)-1) || map(min(yyy)+2,min(xxx)-1) || map(min(yyy)+1,max(xxx)) || map(max(yyy),max(xxx)) || map(max(yyy)+1,max(xxx)))
                                            pass=0;
                                        end
                                    case 3
                                        if map(min(yyy)-1,min(xxx)+1) || map(min(yyy)+2,min(xxx)) || map(min(yyy)+2,min(xxx)+1)
                                            pass=0; 
                                        end
                                    case 4
                                        if max(xxx)==12
                                            pass=0; 
                                        end
                                        if pass && (map(min(yyy),min(xxx)) || map(min(yyy)+1,min(xxx)) || map(min(yyy)+1,max(xxx)+1) || map(max(yyy),max(xxx)+1))
                                            pass=0; 
                                        end
                                end
                            case 4                               
                                switch hic(1)                                    
                                    case 1                                                                 
                                        if max(yyy)>19                                            
                                            pass=0;                                    
                                        end
                                        if pass && (map(min(yyy),min(xxx)+1) || map(max(yyy)+1,max(xxx)) || map(max(yyy)+1,max(xxx)-1) || map(max(yyy)+2,max(xxx)) || map(max(yyy)+2,max(xxx)-1))
                                            pass=0;
                                        end
                                    case 2
                                        if min(xxx)==1
                                            pass=0;
                                        end
                                        if pass && (map(max(yyy)-1,max(xxx)) || map(min(yyy)+1,min(xxx)-1) || map(min(yyy)+2,min(xxx)-1) || map(min(yyy)+3,min(xxx)-1))
                                            pass=0;
                                        end
                                    case 3
                                        if map(min(yyy)-1,min(xxx)) || map(min(yyy)-1,min(xxx)+1) || map(min(yyy)+1,min(xxx)+1) || map(max(yyy)+1,min(xxx)+1)
                                            pass=0;
                                        end
                                    case 4
                                        if max(xxx)==12
                                            pass=0;
                                        end
                                        if pass && (map(min(yyy)+1,min(xxx)) || map(max(yyy),min(xxx)) || map(min(yyy)+1,max(xxx)+1) || map(min(yyy)+2,max(xxx)+1) || map(min(yyy),max(xxx)+1))
                                            pass=0;
                                        end
                                end
                            case 5
                                switch hic(1)
                                    case {1,3}
                                        if map(max(yyy)+1,min(xxx)+1) || map(min(yyy),max(xxx)) || map(min(yyy)-1,max(xxx))
                                            pass=0; 
                                        end
                                    case {2,4}
                                        if min(xxx)==1
                                            pass=0; 
                                        end
                                        if pass && (map(min(yyy)+1,min(xxx)-1) || map(max(yyy),min(xxx)-1) || map(max(yyy)+1,min(xxx)) || map(max(yyy)+1,max(xxx)) || map(max(yyy),max(xxx)))
                                            pass=0; 
                                        end
                                end
                            case 6
                                switch hic(1)
                                    case {1,3}                                                            
                                        if map(min(yyy)+2,min(xxx)+1) || map(min(yyy),min(xxx)) || map(min(yyy)-1,min(xxx))
                                            pass=0;
                                        end
                                    case {2,4}
                                        if max(xxx)==12
                                            pass=0;
                                        end
                                        if pass && (map(max(yyy),min(xxx)) || map(max(yyy)+1,min(xxx)) || map(max(yyy)+1,min(xxx)+1) || map(max(yyy),min(xxx)+2) || map(max(yyy)-1,min(xxx)+2) )
                                            pass=0;
                                        end
                                end
                            case 7
                                switch hic(1)
                                    case 1
                                        if max(yyy)>19
                                            pass=0;
                                        end
                                        if pass && (map(max(yyy)+1,max(xxx)-1) || map(max(yyy)+2,max(xxx)-1) || map(max(yyy)+1,max(xxx)))
                                            pass=0;
                                        end
                                    case 2
                                        if min(xxx)==1
                                            pass=0;
                                        end
                                        if pass && (map(min(yyy)+1,min(xxx)-1) || map(max(yyy),min(xxx)-1) || map(max(yyy)+1,min(xxx)) || map(max(yyy),min(xxx)+1))
                                            pass=0;
                                        end
                                    case 3
                                        if map(min(yyy)-1,min(xxx)+1) || map(max(yyy),min(xxx) || map(max(yyy)+1,min(xxx)+1))
                                            pass=0;
                                        end
                                    case 4
                                        if max(xxx)==12
                                            pass=0;
                                        end
                                        if pass && (map(min(yyy)+1,max(xxx)+1) || map(max(yyy),max(xxx)+1) || map(max(yyy),min(xxx)))
                                            pass=0; 
                                        end
                                end
                        end
                        if pass %finally, if the piece is allowed to flip
                            sound2(3)   %make flip sound
                            if hic(1)==4 %flip by changing hic(1) from 1-> 2-> 3-> 4-> 1-> 2...
                                hic(1)=1;
                            else
                                hic(1)=hic(1)+1;
                            end
                        else
                            sound2(6) %else make error sound
                        end
                    end
                    %after pressing any button we change the current active character to someting else
                    %otherwise the original character will be kept 'pressed' every time we get back to the loop
                    set(hfig,'currentcharacter','l') 
                end
                %after all is said and done, we draw the shape in the main box. the shape's position                
                %in the box depends on the shape, previous position, right/left changes and how
                %far it has fallen down. also in drawshape2 the map matrix gets updated with the new
                %values. map is the current matrix, map2 is the updated one.
                [hh,map2,prev]=drawshape2(chz(1),hic(1),r,d,map,prev);
%                 disp(map2) %this is just for fun
                kilo=map2-map; %kilo is the change of the map after the piece fell
                map=map2;
                if sum(abs(kilo(21,:))) %if there is a change in the final row, this                    
                    break %means the piece hit the ground, break and make another piece
                    %note that break gets out of the loop, so we won't see the piece
                    %blocks getting deleted, or the piece moving any more.
                end
                tt=0; %this is the condition to signal the piece should settle
                yyy=prev(1,:);
                xxx=prev(2,:);                
                ghj=find(yyy==max(yyy)); %this is the maximum y coordinate of the latest piece
                ghj2=find(xxx==min(xxx));
                %the following If statement(s) determine if there is a block below
                %any of the maximum y coordinate blocks of our piece
                if numel(ghj)==1
                    if map(prev(1,ghj)+1,prev(2,ghj));
                        break %if so, break
                    end
                else
                    for klop=1:numel(ghj)
                        if map(prev(1,ghj(klop))+1,prev(2,ghj(klop)))
                            tt=1; %or signal that the piece should settle
                        end
                    end
                end
                %for some pieces, if the sum of all the blocks contained in the rectangle formed by it exceeds 4, it should settle
                ggg=[min(yyy),max(yyy),min(xxx),max(xxx)];
                %for others, we take special cases. the following statements make sure a piece settles when it should,
                %through the care of special cases.
                hjp=1;
                if (chz(1)==3 && hic(1)==4) || (chz(1)==4 && hic(1)==2)           
                    hjp=0;                   
                end
                if ((chz(1)==3 && hic(1)==2) || chz(1)==4 && hic(1)==4)                    
                    if chz(1)==3
                        if map(yyy(4)+1,xxx(4))
                            tt=1;
                        end
                    else
                        if map(yyy(1)+1,xxx(1))
                            tt=1; 
                        end
                    end
                elseif hjp
                    if sum(sum(map(ggg(1):ggg(2),ggg(3):ggg(4))))~=4
                        tt=1;
                    end
                elseif chz(1)==3 && hic(1)==3
                    if map((max(yyy)),min(xxx)) || map(max(yyy),min(xxx)+1)
                        tt=1;
                    end
                elseif chz(1)==4 && hic(1)==3
                    if map((max(yyy)),max(xxx))
                        tt=1; 
                    end
                elseif chz(1)==3 && hic(1)==1
                    if map(max(yyy)+1,min(xxx)+1)
                        tt=1; 
                    end
                elseif chz(1)==4 && hic(1)==1
                    if map(max(yyy)+1,min(xxx)+1)
                       tt=1; 
                    end
                elseif chz(1)==4 && hic(1)==3
                    if map(max(yyy),min(xxx)+1)
                        tt=1; 
                    end                    
                elseif chz(1)==7 && hic(1)==4
                    if map(max(yyy),min(xxx))
                        tt=1;
                    end
                elseif chz(1)==6 && (hic(1)==2 || hic(1)==4)
                    if map(max(yyy),min(xxx))                        
                        tt=1; 
                    end
                elseif chz(1)==5 && (hic(1)==2 || hic(1)==4)
                    if map(max(yyy),max(xxx))                       
                        tt=1; 
                    end
                end
                if tt %finally, if a piece needs to settle, get out of the loop
                    break %note: we could put this break in the place of 'tt=1'
                end       %before.
                d=1+d; %if the piece shouldn't settle, then it will go down another block distance
                pause(timm) %pause so we would see the updated display after all the action                                                              
                
                for ll=1:4 %delete the four blocks of our piece in the main box window
                    delete(hh(ll))
                end
            end %end of main loop 2 (the piece has settled), make another
            sound2(5) %piece settling down sound
            scz=str2double(get(scr,'string'))+5*str2double(get(lvl,'string')); %score+5
            delete(scr) %deleted old score
            scr=text('position',[4.55 4.55],'string',scz,'color','w'); %put up new score
        end %end of main loop 1 (i.e. we're done)
        
        function sound2(a) %this function produces the different sounds, depending on 'a'
            if strcmp(get(mmm,'label'),'Turn On Sound')           
                return  %if we turned the sound off, we get out without producing anything
            end
            t=-4:.01:4; 
            switch a %else we make a sound based on 'a'
                case 1 %move right
                    %we use the MATLAB 'sound(y)' function to produce our sounds,
                    %where y is a vector consisting of numbers generally between -1
                    %and 1. the values of y should also generally be part of a
                    %periodic function, so I tested some different functions and
                    %obtained different sounds.
                    T0=1;
                    t=-4:.01:4;
                    w0=2*pi/T0;
                    s2=1/2;
                    for k=1:140        
                        s2=s2+2*(1-cos(20*k*w0*t-pi/2)+1*sin(20*k*w0*t-pi/2))*10/(1+k^2);
                    end
                    %you can plot(s2(1:50)) to see that it's the fourier series of a saw
                    %wave function, but with very low frequency (high pitch of the sound)
                    %the following is its 'sound'
                    sound(s2)                    
                case 2 %move left
                    T0=1;
                    t=-4:.01:4;
                    w0=2*pi/T0;
                    s2=1/2;
                    for k=1:140        
                        s2=s2+2*(1-cos(10*k*w0*t-pi/2)+1*sin(10*k*w0*t-pi/2))*10/(1+k^2);
                    end
                    %another periodic function with a cool sound
                    sound(s2)
                case 3 %flip piece
                    sound(10*sin(50*t)) %low frequency sine wave
                case 4 %complete line, gravity
                    sound(cos(50*t.^5)*10) %messed up function
                case 5 %settling down
                    sound(cos(50*t.^2)*10) %semi messed up function              
                case 6 %error
                    T0=1;
                    t=-4:.01:4;
                    w0=2*pi/T0;
                    s2=1/2;
                    for k=1:140        
                        s2=s2+2*(1-cos(1*k*w0*t-pi/2)+1*sin(1*k*w0*t-pi/2))*10/(1+k^2);
                    end
                    sound(s2) %plot and see
% 					equake(gcf,1,1,0)
            end
        end
    end

    function sound4(a)   %another sound function (used for the music)
        t=-4:.01:4;      
        %it makes a different sound based on the frequency you give it
        sound(cos(a*t)*10)
    end

    function h=drawshape1(a,b) %function that draws the pieces in the NEXT box
        %we draw the pieces using the patch function to draw four blocks for each piece.
        %using patch, you speciy  four points (tips of the square) in the x and
        %y coordinates, and patch fills the square meant by them. for example this command
        %patch([0 0 1 1],[0 1 1 0]) draws the square having a side 1 and starts
        %from the origin (the first argument is x, second is y).       
        xp=[0 0 .125 .125]; %this is how much the x argument increments when patching a square
        yp=[0 .25 .25 0]; %how much the y argument increments        
        xi=.125; %the x increment when moving to a square on the right (space between the starts of two squares)
        yi=.25; % space between the starts of two blocks on top of each other
        X=4.25; %starting position for x
        Y=12.2; %starting position for y
        %note: if you want to see how patch works more clearly, change the colours of the axes and use grid.
        switch a %a is the piece to be drawn, b is its orientation
            case 1 %4 block line
                switch b 
                    case {1,3} %horizontal
                        xshape=[0 xi xi*2 xi*3]; %this is how it looks like, 4 blocks next to each other
                        yshape=zeros(1,4);       %in the same y direction from the origin
                    case {2,4} %vertical
                        xshape=repmat(1.5*xi,1,4); %four blocks having the same x
                        yshape=[0 -yi -2*yi -3*yi]; %and consecutive y
                end
            case 2 %square
                xshape=repmat([xi 2*xi],1,2); %a square, like this: [xi xi 2*xi 2*xi]
                yshape=repmat([0 -yi],2,1);   %                     [0 -yi   0   -yi]
            case 3 %inverted L
                switch b 
                    case 1 %this shape: |__
                        xshape=[xi xi 2*xi 3*xi];
                        yshape=[0 -yi -yi -yi];
                    case 2 %upside down L, like this:.--
                        xshape=[xi xi xi 2*xi]; %    |
                        yshape=[-yi -2*yi 0 0]; %    |              
                    case 3 %case 1 turned 180 degrees
                        xshape=[xi xi*2 3*xi 3*xi];
                        yshape=[0 0 0 -yi];                  
                    case 4 %inverted L
                        xshape=[xi 2*xi 2*xi 2*xi];
                        yshape=[-2*yi -2*yi -yi 0];  
                end
            case 4 %L
                switch b
                    case 1
                        xshape=[xi xi*2 xi*3 xi*3];
                        yshape=[-yi -yi -yi 0];             
                    case 2
                        xshape=[xi*3 2*xi 2*xi 2*xi];
                        yshape=[-2*yi -2*yi -yi 0];             
                    case  3
                        xshape=[xi xi 2*xi 3*xi];
                        yshape=[-yi 0 0 0];             
                    case 4                        
                        xshape=[xi 2*xi 2*xi 2*xi];
                        yshape=[0 -yi 0 -2*yi]; 
                end
            case 5 %Z shape
                switch b
                    case {1,3}
                        xshape=[xi xi*2 xi*2 xi*3];
                        yshape=[0 0 -yi -yi];
                    case {2,4}
                        xshape=[xi*2 xi*2 xi*3 xi*3]-xi;
                        yshape=[-yi*2 -yi -yi 0];                            
                end
            case 6 %S shape
                switch b
                    case {1,3}                
                        xshape=fliplr([xi xi*2 xi*2 xi*3]);
                        yshape=[0 0 -yi -yi];
                    case {2,4}
                        xshape=fliplr([xi*2 xi*2 xi*3 xi*3])-xi;
                        yshape=([-yi*2 -yi -yi 0]);                  
                end
            case 7 %don't know the name of this one, but it's the 'fork' style shape
                switch b
                    case 1                
                        xshape=[xi xi*2 xi*2 xi*3];
                        yshape=[-yi -yi 0 -yi];
                    case 2
                        xshape=[xi xi xi xi*2];
                        yshape=[0 -yi -yi*2 -yi];
                    case 3
                        xshape=[xi xi*2 xi*2 xi*3];
                        yshape=[0 0 -yi 0];
                    case 4                        
                        xshape=[xi xi*2 xi*2 xi*2];
                        yshape=[-yi 0 -yi -yi*2];
                end
        end
        XX=zeros(1,4); %this is called prallocating. if you don't make these initialized
        YY=zeros(1,4); %matrices, the code runs somewhat slower, so make sure you reserve
        h=zeros(1,4);  %some memory beforehand using preallocation
        %after specifying the shape of our pieces, we specify the exact coordinates
        for j=1:4 %four block for each piece
            for i=1:4 %four values for each block
                XX(i)=X+xp(i)+xshape(j); %the x argument in the patch command. add starting position with
                                         %x increment and shape x increment.
                YY(i)=Y+yp(i)+yshape(j); %the y argument. since the pieces in NEXT never move, this is simpler
                                         %than the drawshape2 function
            end
            h(j)=patch(XX,YY,[1,1,1]); %patch each block with the colour white
        end
    end

    function musicc(a) %the music function
        if strcmp(get(mmm,'label'),'Turn On Sound') %if sound is turned off,
            return %come back from here empty handed
        end
        switch a
            case 1 %game over song        
                %the way I made this is as follows: the tab for this song on a bass guitar is
                %something like this: 3,3,3,3,6,5,5,3,3,2,3 on the A string.
                %so using the sound4 function and trying different frequencies, I got 'right'
                %sounds for 3 using sound4(10), and for 6 sound4(12). then, using ratios and more
                %experminenting, 5 becomes sound4(11.5) and 2 sound4(9.5). after the sounds were               
                %known, I used pause with different values to make the timing.
                sound4(10)
                pause(.5*.75)
                sound4(10)
                pause(.5*.5)
                sound4(10)
                pause(.5*.25)
                sound4(10)
                pause(.5*.75)
                sound4(12)
                pause(.5*.5) 
                sound4(11.5)
                pause(.5*.25)      
                sound4(11.5)               
                pause(.5*.5) 
                sound4(10)
                pause(.5*.25)
                sound4(10)
                pause(.5*.5) 
                sound4(9.5)
                pause(.5*.25)
                sound4(10)
            case 2 %end level song
                sound4(40) %just a nice mesh of sounds
                pause(.15)
                sound4(40)        
                pause(.15)
                sound4(40)        
                pause(.15)
                sound4(40)        
                pause(.15)
                sound4(40+9/2)  
        end
    end

    function [hh,map,prev]=drawshape2(a,b,r,d,map,dep) %the piece drawing function in the main box
        %if you haven't read the comments for drawshape1, I suggest you do now. drawshape2 is similar.
        xp=[0 0 .25 .25]; %the x increments are higher because the blocks are bigger.
        xi=.25;           %use grid and change axes colours to see this clearly.
        yi=.5;
        yp=[0 .5 .5 0];
        X=2;              %starting positions
        Y=12;
        switch a %same shape symbols as before
            case 1
                switch b
                    case {1,3}
                        xshape=[0 xi xi*2 xi*3];
                        yshape=zeros(1,4);
                    case {2,4}
                        xshape=repmat(2*xi,1,4); %notice here we couldn't use 1.5*xi, because that
                                                 %wouldn't be an integer multiple of the x increment
                        yshape=[0 -yi -2*yi yi];
                end
            case 2
                xshape=repmat([xi 2*xi],1,2);
                yshape=repmat([0 -yi],2,1);
            case 3
                switch b
                    case 1
                        xshape=[xi xi 2*xi 3*xi];
                        yshape=[0 -yi -yi -yi];
                    case 2
                        xshape=[xi xi xi 2*xi]+xi;
                        yshape=[-yi -2*yi 0 0];                
                    case 3
                        xshape=[xi xi*2 3*xi 3*xi];
                        yshape=[0 0 0 -yi]-yi;                  
                    case 4
                        xshape=[xi 2*xi 2*xi 2*xi];
                        yshape=[-2*yi -2*yi -yi 0];                 
                end               
            case 4       
                switch b
                    case 1
                        xshape=[xi xi*2 xi*3 xi*3];
                        yshape=[-yi -yi -yi 0];             
                    case 2
                        xshape=[xi*3 2*xi 2*xi 2*xi];
                        yshape=[-2*yi -2*yi -yi 0];             
                    case  3
                        xshape=[xi xi 2*xi 3*xi];
                        yshape=[-yi 0 0 0]-yi;             
                    case 4
                        xshape=[xi 2*xi 2*xi 2*xi];
                        yshape=[0 -yi 0 -2*yi]; 
                end
            case 5
                switch b
                    case {1,3}
                        xshape=[xi xi*2 xi*2 xi*3]-xi;
                        yshape=[0 0 -yi -yi]-yi;
                    case {2,4}
                        xshape=[xi*2 xi*2 xi*3 xi*3]-xi;
                        yshape=[-yi*2 -yi -yi 0];                            
                end
            case 6
                switch b
                    case {1,3}                
                        xshape=fliplr([xi xi*2 xi*2 xi*3]);
                        yshape=[0 0 -yi -yi]-yi;
                    case {2,4}
                        xshape=fliplr([xi*2 xi*2 xi*3 xi*3])-xi;
                        yshape=([-yi*2 -yi -yi 0]);                  
                end
            case 7
                switch b
                    case 1                
                        xshape=[xi xi*2 xi*2 xi*3];
                        yshape=[-yi -yi 0 -yi];
                    case 2
                        xshape=[xi xi xi xi*2]+xi;
                        yshape=[0 -yi -yi*2 -yi];
                    case 3
                        xshape=[xi xi*2 xi*2 xi*3];
                        yshape=[0 0 -yi 0]-yi;
                    case 4
                        xshape=[xi xi*2 xi*2 xi*2];
                        yshape=[-yi 0 -yi -yi*2];
                end
        end
        %due to the pieces being able to move, there are some new commands
        xshape=xshape+r*xi; %moves the x coordinates in multiples of the x increment (going right/left)
        yshape=yshape-d*yi; %moves the y coordinates in multiples of the y increment (going down)
        
        %next, some new commands are seen. these are to make the matrix and figure window equal
        for j=1:4 %this loop makes the map update by deleting the previously occupied 1 elements
            if dep~=666 %remember this? dep==666 means it's the first time and you need to skip this
                map(dep(1,(j)),dep(2,(j)))=0; %dep are the xy coordinates of the previous shape location
            end
        end
        %the drawing loop
        XX=zeros(1,4); %prelocating
        YY=zeros(1,4);
        hh=zeros(1,4);
        prev=zeros(2,4);
        for j=1:4 %four blocks
            for i=1:4 %four coordinates    
                XX(i)=X+xp(i)+xshape(j); %same as before
                YY(i)=Y+yp(i)+yshape(j);                
            end
            %the map puts a 1 in the location of every new block
            map(-round(yshape(j)/yi)+1,round(xshape(j)/xi)+5)=1;
            %we take the x and y coordinates of the newly shaped block and put them in prev
            prev(1,j)=-round(yshape(j)/yi)+1; %these become the variable dep the next time
            prev(2,j)=round(xshape(j)/xi)+5;  %we're here
            %and draw the block in the main figure window
            hh(j)=patch(XX,YY,[1,1,1]);            
        end
        %note: notice how we only see the 4 blocks together, and not individually drawn. this
        %is because display updates with the pause command, which appears after all the blocks
        %are drawn.
    end

    function [rn,yn]=chk(map) %checks for any completed line
        yn=0;       %initially, there are no completed lines
        for rn=21:-1:2        %scans all rows of the matrix
            if sum(map(rn,:))==12    %if any row adds to 12, it means it is full of ones
                yn=1;                
                break               
            end
        end
    end

    function map=hrav(map) %this function performs gravity
        for i=2:21 %all rows. you can change the numbers here for 'custom' gravity
            for j=1:12 %scan all columns
                if ~map(i,j) && map(i-1,j) %if there is a 0 with a 1 above it
                    map(2:i,j)=map(1:i-1,j); %make that entire column fall down
                end
            end
        end
    end
end



Contact us at files@mathworks.com