Текущая версия |
Ваш текст |
Строка 1: |
Строка 1: |
− | ==Описание==
| |
− |
| |
− | Реализация копьютерной игры тетрис на языке программирования JavaScript
| |
− |
| |
− | Исполнитель: [[Бакута Артём|Бакута Артём]]
| |
− |
| |
− | Группа 13632/1 Кафедра Теоретической механики
| |
− |
| |
− | Файл:[[https://csspbstu-my.sharepoint.com/:w:/g/personal/bakuta_ad_edu_spbstu_ru/EUhgTweJoiRGkRsNAJYFuPEB3PDm0ceEIPsR2nG1jQYF-g?e=lI4tSY]]
| |
− |
| |
− | ==Визуализация==
| |
| {{#widget:Iframe |url=http://tm.spbstu.ru/htmlets/Bakuta_AD/tetris.html |width=800 |height=650 |border=0 }} | | {{#widget:Iframe |url=http://tm.spbstu.ru/htmlets/Bakuta_AD/tetris.html |width=800 |height=650 |border=0 }} |
− |
| |
− | ==Код программы==
| |
− | <div class="mw-collapsible mw-collapsed">
| |
− | '''Код программы на языке JavaScript:''' <div class="mw-collapsible-content">
| |
− | <syntaxhighlight lang="javascript" line start="1" enclose="div">
| |
− | // indicates the table in the page, this table is the main panel that will display the game
| |
− | var tbl;
| |
− | // Game state 0: not started; 1 run; 2 abort;
| |
− | var status = 0;
| |
− | // timer, the timer will do moveDown operation
| |
− | var timer;
| |
− | //fraction
| |
− | var score = 0;
| |
− | //True if this is the first piece
| |
− | // Needed to generate the next piece
| |
− | var ff;
| |
− | //area is an 18*10 array that also corresponds to the table's table. Initially 0, if occupied, 1
| |
− | // Counter for droped lines
| |
− | var DropedLines = 0;
| |
− | //Speed of the game from 1 to 0.3
| |
− | var speed = 1;
| |
− | //Level of current game
| |
− | var level = 1;
| |
− | // Incresing the score if lines are dropping in a raw
| |
− | var factor = 0;
| |
− |
| |
− | var isDown = true;
| |
− | var area = new Array(18);
| |
− | for(var i=0;i<18;i++)
| |
− | {
| |
− | area[i] = new Array(10);
| |
− | }
| |
− | for(var i=0;i<18;i++)
| |
− | {
| |
− | for(var j=0; j<10; j++)
| |
− | {
| |
− | area[i][j] = 0;
| |
− | }
| |
− | }
| |
− |
| |
− | // The currently active square, which can be moved left and right, variant. When it bottoms out, the area will be updated;
| |
− | var activeBlock;
| |
− | var nextBlock;
| |
− | // Produce a square shape with 7 basic shapes.
| |
− | function generateBlock()
| |
− | {
| |
− | activeBlock = null;
| |
− | document.getElementById("speed").innerText = " " + speed;
| |
− | if (ff == false)
| |
− | {
| |
− | activeBlock = nextBlock;
| |
− | eraseNext();
| |
− | }
| |
− | else
| |
− | {
| |
− | activeBlock = gen();
| |
− | }
| |
− | nextBlock = gen();
| |
− |
| |
− | //Check if the four small squares just produced can be placed in the initialized position.
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | if(!isCellValid(activeBlock[i].x, activeBlock[i].y))
| |
− | {
| |
− | return false;
| |
− | }
| |
− | }
| |
− | ff = false;
| |
− | return true;
| |
− | }
| |
− |
| |
− | // Randomly generate 0-6 array, representing 7 forms.
| |
− | function gen()
| |
− | {
| |
− | var block = new Array(4);
| |
− | var t = (Math.floor(Math.random()*20)+1)%19;
| |
− | // The repetition prevents an appearance the same blocks many times in a raw
| |
− | switch(t)
| |
− | {
| |
− | case 0:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:0, y:5};
| |
− | block[3] = {x:1, y:5};
| |
− | break;
| |
− | }
| |
− | case 1:
| |
− | {
| |
− | block[0] = {x:0, y:3};
| |
− | block[1] = {x:0, y:4};
| |
− | block[2] = {x:0, y:5};
| |
− | block[3] = {x:0, y:6};
| |
− | break;
| |
− | }
| |
− | case 2:
| |
− | {
| |
− | block[0] = {x:0, y:5};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:2, y:4};
| |
− | break;
| |
− | }
| |
− | case 3:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:2, y:5};
| |
− | break;
| |
− | }
| |
− | case 4:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:1, y:6};
| |
− | break;
| |
− | }
| |
− | case 5:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:2, y:4};
| |
− | block[3] = {x:2, y:5};
| |
− | break;
| |
− | }
| |
− | case 6:
| |
− | {
| |
− | block[0] = {x:0, y:5};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:1, y:6};
| |
− | break;
| |
− | }
| |
− | case 7:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:0, y:5};
| |
− | block[3] = {x:1, y:5};
| |
− | break;
| |
− | }
| |
− | case 8:
| |
− | {
| |
− | block[0] = {x:0, y:3};
| |
− | block[1] = {x:0, y:4};
| |
− | block[2] = {x:0, y:5};
| |
− | block[3] = {x:0, y:6};
| |
− | break;
| |
− | }
| |
− | case 9:
| |
− | {
| |
− | block[0] = {x:0, y:5};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:2, y:4};
| |
− | break;
| |
− | }
| |
− | case 10:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:2, y:5};
| |
− | break;
| |
− | }
| |
− | case 11:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:1, y:6};
| |
− | break;
| |
− | }
| |
− | case 12:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:2, y:4};
| |
− | block[3] = {x:2, y:5};
| |
− | break;
| |
− | }
| |
− | case 13:
| |
− | {
| |
− | block[0] = {x:0, y:5};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:1, y:6};
| |
− | break;
| |
− | }
| |
− | case 14:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:0, y:5};
| |
− | block[3] = {x:1, y:5};
| |
− | break;
| |
− | }
| |
− | case 15:
| |
− | {
| |
− | block[0] = {x:0, y:3};
| |
− | block[1] = {x:0, y:4};
| |
− | block[2] = {x:0, y:5};
| |
− | block[3] = {x:0, y:6};
| |
− | break;
| |
− | }
| |
− | case 16:
| |
− | {
| |
− | block[0] = {x:0, y:5};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:2, y:4};
| |
− | break;
| |
− | }
| |
− | case 17:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:2, y:5};
| |
− | break;
| |
− | }
| |
− | case 18:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:1, y:5};
| |
− | block[3] = {x:1, y:6};
| |
− | break;
| |
− | }
| |
− | case 19:
| |
− | {
| |
− | block[0] = {x:0, y:4};
| |
− | block[1] = {x:1, y:4};
| |
− | block[2] = {x:2, y:4};
| |
− | block[3] = {x:2, y:5};
| |
− | break;
| |
− | }
| |
− | }
| |
− | return block;
| |
− | }
| |
− | //Move Downward
| |
− | function moveDown()
| |
− | {
| |
− | // Check the bottom boundary.
| |
− | if(checkBottomBorder())
| |
− | {
| |
− | //No bottoming, erase the current graphic,
| |
− | erase();
| |
− | // Update the current drawing coordinates
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | activeBlock[i].x = activeBlock[i].x + 1;
| |
− | }
| |
− | // Repaint the current graphics
| |
− | paint();
| |
− | }
| |
− | // bottoming out,
| |
− | else
| |
− | {
| |
− | // Stop the current timer, that is, stop automatically moving down.
| |
− | clearInterval(timer);
| |
− | // Update the area array.
| |
− | updatearea();
| |
− | // Consumer
| |
− | var lines = deleteLine();
| |
− | //if there is a break, then
| |
− | if(lines!=0){
| |
− | DropedLines++;
| |
− | factor++;
| |
− | // Update the score
| |
− | score = score + lines*10*factor;
| |
− | for (var i = 1; i < 10; i++)
| |
− | {
| |
− | if (score - i*50 >= 0) { level = i+1; }
| |
− | }
| |
− | updatescore();
| |
− | // Wipe the entire panel
| |
− | erasearea();
| |
− | //Redraw panel
| |
− | paintarea();
| |
− | }
| |
− | else { factor = 0 }
| |
− |
| |
− | if (level > 1)
| |
− | {
| |
− | speed = 1-0.1*level;
| |
− | }
| |
− |
| |
− | //Generate a new graphic and determine if it can be placed in the original position.
| |
− | if(!generateBlock())
| |
− | {
| |
− | alert("Game over!");
| |
− | status = 2;
| |
− | return;
| |
− | }
| |
− | paint();
| |
− | //timer, execute moveDown every second
| |
− | timer = setInterval(moveDown, 1000*speed)
| |
− | }
| |
− | }
| |
− |
| |
− | function straightDown()
| |
− | {
| |
− | var currentBlock = activeBlock;
| |
− | for(var i = 0; i < 18; i++)
| |
− | {
| |
− | if (currentBlock == activeBlock)
| |
− | {
| |
− | moveDown();
| |
− | }
| |
− | else
| |
− | {
| |
− | break;
| |
− | }
| |
− | }
| |
− |
| |
− |
| |
− | }
| |
− |
| |
− | //Left move
| |
− | function moveLeft()
| |
− | {
| |
− | if(checkLeftBorder())
| |
− | {
| |
− | erase();
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | activeBlock[i].y = activeBlock[i].y - 1;
| |
− | }
| |
− | paint();
| |
− | }
| |
− | }
| |
− | //Right move
| |
− | function moveRight()
| |
− | {
| |
− | if(checkRightBorder())
| |
− | {
| |
− | erase();
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | activeBlock[i].y = activeBlock[i].y + 1;
| |
− | }
| |
− | paint();
| |
− | }
| |
− | }
| |
− | //rotate, because there may be squares over the existing squares after the rotation.
| |
− | // First use a tmpBlock, copy the contents of activeBlock to tmpBlock,
| |
− | // Try to rotate tmpBlock, if the detection after rotation found no squares conflict, then
| |
− | // Give the value of the rotated tmpBlock to activeBlock.
| |
− | function rotate()
| |
− | {
| |
− | var tmpBlock = new Array(4);
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | tmpBlock[i] = {x:0, y:0};
| |
− | }
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | tmpBlock[i].x = activeBlock[i].x;
| |
− | tmpBlock[i].y = activeBlock[i].y;
| |
− | }
| |
− | // Calculate the center point of the four points first, then the four points are rotated 90 degrees around the center.
| |
− | var cx = Math.round((tmpBlock[0].x + tmpBlock[1].x + tmpBlock[2].x + tmpBlock[3].x)/4);
| |
− | var cy = Math.round((tmpBlock[0].y + tmpBlock[1].y + tmpBlock[2].y + tmpBlock[3].y)/4);
| |
− | // The main algorithm of rotation. Can be broken down to understand.
| |
− | // First assume that the rotation around the source point. Then add the coordinates of the center point.
| |
− |
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | tmpBlock[i].x = cx+cy-activeBlock[i].y;
| |
− | tmpBlock[i].y = cy-cx+activeBlock[i].x;
| |
− | }
| |
− | // Check whether the rotation of the rear grid is legal.
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | if(!isCellValid(tmpBlock[i].x,tmpBlock[i].y))
| |
− | {
| |
− | return;
| |
− | }
| |
− | }
| |
− | //if it is legal, erase
| |
− | erase();
| |
− | // Reassign the activeBlock.
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | activeBlock[i].x = tmpBlock[i].x;
| |
− | activeBlock[i].y = tmpBlock[i].y;
| |
− | }
| |
− | //Redraw.
| |
− | paint();
| |
− | }
| |
− | //Check the left border and try to move one to the left to see if it is legal.
| |
− | function checkLeftBorder()
| |
− | {
| |
− | for(var i=0; i<activeBlock.length; i++)
| |
− | {
| |
− | if(activeBlock[i].y==0)
| |
− | {
| |
− | return false;
| |
− | }
| |
− | if(!isCellValid(activeBlock[i].x, activeBlock[i].y-1))
| |
− | {
| |
− | return false;
| |
− | }
| |
− | }
| |
− | return true;
| |
− | }
| |
− | //Check the right border and try to move one to the right to see if it is legal.
| |
− | function checkRightBorder()
| |
− | {
| |
− | for(var i=0; i<activeBlock.length; i++)
| |
− | {
| |
− | if(activeBlock[i].y==9)
| |
− | {
| |
− | return false;
| |
− | }
| |
− | if(!isCellValid(activeBlock[i].x, activeBlock[i].y+1))
| |
− | {
| |
− | return false;
| |
− | }
| |
− | }
| |
− | return true;
| |
− | }
| |
− | //Check the bottom boundary and try to move one down to see if it is legal.
| |
− | function checkBottomBorder()
| |
− | {
| |
− | for(var i=0; i<activeBlock.length; i++)
| |
− | {
| |
− | if(activeBlock[i].x==17)
| |
− | {
| |
− | return false;
| |
− | }
| |
− | if(!isCellValid(activeBlock[i].x+1, activeBlock[i].y)){
| |
− | return false;
| |
− | }
| |
− | }
| |
− | return true;
| |
− | }
| |
− | //Check if the coordinates of (x,y) already exist in the area, and the existence indicates that the square is illegal.
| |
− | function isCellValid(x, y)
| |
− | {
| |
− | if(x>17||x<0||y>9||y<0)
| |
− | {
| |
− | return false;
| |
− | }
| |
− | if(area[x][y]==1)
| |
− | {
| |
− | return false;
| |
− | }
| |
− | return true;
| |
− | }
| |
− | //erase
| |
− | function erase()
| |
− | {
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | tbl.rows[activeBlock[i].x].cells[activeBlock[i].y].style.backgroundColor="white";
| |
− | }
| |
− | }
| |
− |
| |
− | function eraseNext()
| |
− | {
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | tbl2.rows[nextBlock[i].x].cells[nextBlock[i].y-3].style.backgroundColor="#EBEBEB";
| |
− | }
| |
− | }
| |
− |
| |
− | // paint activity graphics
| |
− | function paint()
| |
− | {
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | tbl.rows[activeBlock[i].x].cells[activeBlock[i].y].style.backgroundColor="#CC3333";
| |
− | }
| |
− | for(var i = 0; i < 4; i++)
| |
− | {
| |
− | tbl2.rows[nextBlock[i].x].cells[nextBlock[i].y-3].style.backgroundColor="#CC3333";
| |
− | }
| |
− | }
| |
− | // Update the area array
| |
− | function updatearea()
| |
− | {
| |
− | for(var i=0; i<4; i++)
| |
− | {
| |
− | area[activeBlock[i].x][activeBlock[i].y]=1;
| |
− | }
| |
− | }
| |
− | // Consumer
| |
− | function deleteLine()
| |
− | {
| |
− | var lines = 0;
| |
− | for(var i=0; i<18; i++)
| |
− | {
| |
− | var j=0;
| |
− | for(; j<10; j++)
| |
− | {
| |
− | if(area[i][j]==0)
| |
− | {
| |
− | break;
| |
− | }
| |
− | }
| |
− | if(j==10)
| |
− | {
| |
− | lines++;
| |
− | if(i!=0)
| |
− | {
| |
− | for(var k=i-1; k>=0; k--)
| |
− | {
| |
− | area[k+1] = area[k];
| |
− | }
| |
− | }
| |
− | area[0] = generateBlankLine();
| |
− | }
| |
− | }
| |
− | return lines;
| |
− | }
| |
− | // Wipe the entire panel
| |
− | function erasearea()
| |
− | {
| |
− | for(var i=0; i<18; i++)
| |
− | {
| |
− | for(var j=0; j<10; j++)
| |
− | {
| |
− | tbl.rows[i].cells[j].style.backgroundColor = "white";
| |
− | }
| |
− | }
| |
− | }
| |
− | // Redraw the entire panel
| |
− | function paintarea()
| |
− | {
| |
− | for(var i=0;i<18;i++)
| |
− | {
| |
− | for(var j=0; j<10; j++)
| |
− | {
| |
− | if(area[i][j]==1)
| |
− | {
| |
− | tbl.rows[i].cells[j].style.backgroundColor = "#CC3333";
| |
− | }
| |
− | }
| |
− | }
| |
− | }
| |
− | //Generate a blank line.
| |
− | function generateBlankLine()
| |
− | {
| |
− | var line = new Array(10);
| |
− | for(var i=0; i<10; i++)
| |
− | {
| |
− | line[i] = 0;
| |
− | }
| |
− | return line;
| |
− | }
| |
− | // Update the score
| |
− | function updatescore()
| |
− | {
| |
− | document.getElementById("score").innerText = " " + score;
| |
− | document.getElementById("lines").innerText = " " + DropedLines;
| |
− | document.getElementById("level").innerText = " " + level;
| |
− | }
| |
− | // keyboard control
| |
− | function keyControl()
| |
− | {
| |
− | if(status !=1 )
| |
− | {
| |
− | return;
| |
− | }
| |
− | var code = event.keyCode;
| |
− | switch(code)
| |
− | {
| |
− | case 32:
| |
− | {
| |
− | straightDown();
| |
− | break;
| |
− | }
| |
− |
| |
− | case 37:
| |
− | {
| |
− | moveLeft();
| |
− | break;
| |
− | }
| |
− | case 38:
| |
− | {
| |
− | rotate();
| |
− | break;
| |
− | }
| |
− | case 39:
| |
− | {
| |
− | moveRight();
| |
− | break;
| |
− | }
| |
− | case 40:
| |
− | {
| |
− | moveDown();
| |
− | break;
| |
− | }
| |
− | }
| |
− | }
| |
− |
| |
− |
| |
− | //Start
| |
− | function begin(e)
| |
− | {
| |
− | e.disabled = true;
| |
− | ff = true;
| |
− | status = 1;
| |
− | tbl = document.getElementById("area");
| |
− | tbl2 = document.getElementById("NextPiece");
| |
− | if(!generateBlock())
| |
− | {
| |
− | alert("Game over!");
| |
− | status = 2;
| |
− | return;
| |
− | }
| |
− | paint();
| |
− | timer = setInterval(moveDown,1000*speed);
| |
− | }
| |
− | document.onkeydown=keyControl;
| |
− |
| |
− | </syntaxhighlight>
| |
− | </div>
| |