(재) 테트리스 구현


43

클래식 비디오 게임다시 구현 하기 위해 커뮤니티에 Tetris를 가장 잘 구현하도록 초대하고 싶습니다 .

여기에 이미지 설명을 입력하십시오
참고로, 공식 NES 버전의 테트리스 스크린 샷.

필요한 기능

  • 합리적인 점수 시스템이 마련되어 있어야합니다. 현재 점수는 항상 표시되어야합니다.
  • 나타날 다음 작품은 어떤 식 으로든 표시되어야합니다.
  • 7 개의 테트로 미노 분포는 상당히 균일해야합니다 (즉, 의사 무작위 선택).
  • 사용자는 현재 조각을 양방향으로 회전시키고 하강을 가속화 할 수 있어야합니다.
  • 게임이 끝나면 게임이 끝났음을 분명히 표시해야합니다.
  • 소스 코드는 구조적이고 이해하기 쉬워야합니다.

선택적 기능

  • 일정 횟수의 클리어 (예 : 난이도 증가) 후 속도 저하 및 라인 당 클리어 점수 (속도에 비례).
  • 중량. 블록이 간격을두고 떠 다닐 수있는 '고전적인'중력을 구현하도록 선택할 수도 있고, 라인 클리어를 통해 원래의 테트로 미노와 분리 된 블록이 열릴 수있는 '홍수 채우기'중력을 구현할 수도 있습니다. 격차.
  • 이름 입력으로 높은 점수.
  • 라인 후 애니메이션 및 / 또는 새로운 높은 점수를 얻은 후 애니메이션.

한계

  • 사용 된 모든 라이브러리 ( jQuery , PyGame 등)는 자유롭게 사용할 수 있어야합니다.
  • 소스 코드 크기는 공백과 주석을 제외하고 4096 바이트를 초과하지 않아야합니다. 높은 점수와 같이 생성 된 파일을 제외한 모든 외부 리소스 (데이터 파일, 이미지 등)가 코드 길이에 추가됩니다.
    나는 이것이 다소 임의적 인 제한이라는 것을 알고있다. 저의 주요 목표는 기존 구현의 복사 붙여 넣기를 권장하지 않고 간결함과 자체 격리를 장려하는 것입니다.

승리 기준

이 도전은 인기 콘테스트 로 판단되며 , 가장 많은 찬사를받은 작품이 우승자로 선정됩니다. 업 보팅시 사용자는 위에서 언급 한 요구 사항을 충분히 충족한다고 생각되는 모든 제출물을 업 보트 할 것을 권장합니다.

우승자는 첫 번째 유효한 솔루션이 나온 후 2 주 이내에 선정됩니다. 또한, 나는이 질문이받는 투표 수에 대략 비례하여 우승자에게 현상금을 부여 할 것입니다 ( 10 * #votes가장 가까운 50까지 반올림). 2 주 기간이 만료 된 후 동점 일 경우 경쟁 기간은 1 주일 연장됩니다. 동점 인 경우 최종 투표권을 갖습니다.

설명을 요청하십시오. 최고의 구현이 이길 수 있습니다!


@ moose 소스 코드를 의미합니다. 이를 반영하여 질문이 업데이트되었습니다.
primo

Primo, 초기 버전을 붙여넣고 나중에 업데이트 할 수 있습니까?
Henrik Mühe

@ HenrikMühe 마음에 들지 않지만 필요한 기능 세트를 충족하지 않으면 타이머가 시작되지 않습니다.
primo

나는 이것을 시도하고 싶을 것이다. 아마도 SDL로 할 것입니다 (이전에 사용하지는 않았지만 더 재미있게 만들 것입니다!).
벤 리차드

그래도 Tetris의 QBASIC 구현을 찾을 수 있다면! :)
Ben Richards

답변:


19

시도 : http://tetris.muehe.org

업데이트 글로벌 높은 점수가있다. 그것을 때리는 것을 즐기십시오-또는 대안으로 해킹하십시오 :-)

스크린 샷 : 활동중인 테트리스

CoffeeScript 및 HTML 버전은 내가 아는 한 최고의 요구 사항을 충족해야합니다.

$ make stats
4095

Github https://github.com/henrik-muehe/tetris

풍모

  • 지수 점수
  • 다음 조각 표시기
  • 공정한 무작위성
  • 회전, 이동 및 드롭 다운
  • 게임 오버 표시
  • 서버 백엔드가있는 최고 점수 쉽게 위조 수 없습니다.

큰! 그러나 작은 버그 : 구덩이 바닥에서 여러 행이 완료되면 그중 하나가 사라지지 않습니다.
manatwork

@manatwork 당신은 행 중 하나가 사라질 때 가장 아래쪽에있는 것을 의미합니까? 나는 그것으로 볼 것이다, 스크린 샷 굉장 :)이 될 것입니다
헨릭 Mühe

그것은 아주 좋은 구현입니다. 한 가지 요청 : 어떤 기능이 구현되었는지 나열 할 수 있습니까? 더 많은 솔루션이 제출되면 비교가 쉬워집니다.
primo April

1
@ user2023370, 그것은 바이트이지만 ...
Henrik Mühe

1
링크가 작동하지 않습니다.
Outgolfer Erik

18

파스칼

FreePascal 2.6.2에서 개발되었으며 Turbo Pascal 6.0으로 컴파일해야합니다. 외부 자원이없는 Crt 장치 만 사용됩니다.

program tetris;

  uses
    Crt;

  const
    width = 10;                                  { playing field width }
    height = 20;                                 { playing field height }

  const
    piece: array [1..7, 1..3, 0..1] of ShortInt = ( { piece shapes : piece, element, coordinate }
      (( 1, 0), ( 1, 1), ( 0, 1)),               { O }
      ((-1, 0), ( 1, 0), ( 2, 0)),               { I }
      (( 0, 1), ( 1, 0), ( 2, 0)),               { L }
      (( 0, 1), (-1, 0), (-2, 0)),               { J }
      ((-1, 0), ( 1, 0), ( 0, 1)),               { T }
      (( 1, 0), ( 0, 1), (-1, 1)),               { S }
      ((-1, 0), ( 0, 1), ( 1, 1))                { Z }
    );
    color: array [1..7, 0..1] of Byte = (        { piece colors : foreground, background }
      (Yellow,       Brown),                     { O }
      (LightCyan,    Cyan),                      { I }
      (LightBlue,    Blue),                      { L }
      (White,        LightGray),                 { J }
      (LightMagenta, Magenta),                   { T }
      (LightGreen,   Green),                     { S }
      (LightRed,     Red)                        { Z }
    );

  var
    area: array [1..width + 2, 1..height + 1] of Byte; { playing field }
    coord: array [0..3, 0..1] of Byte;           { precalculated element coordinates }
    played,                                      { played pieces count }
    removed,                                     { completed lines count }
    level,                                       { current level }
    score: LongInt;                              { accumulated score }
    time,                                        { current level delay }
    wait,                                        { current piece delay }
    made,                                        { completed lines in current level }
    multi,                                       { multiline count }
    screen,                                      { original screen size }
    i, j, j2: Word;                              { counters }
    current,                                     { current piece }
    next,                                        { next piece }
    x,                                           { horizontal coordinate }
    y,                                           { vertical coordinate }
    position,                                    { rotation position }
    k: Byte;                                     { pressed key }
    ok: Boolean;                                 { aggregated condition }

{ precalculates the give piece's elements coordinates }
  procedure coordinate(current, x, y, position: Byte);
  begin
    coord[0, 0] := x;
    coord[0, 1] := y;
    for i := 1 to 3 do begin
      coord[i, 0] := x + piece[current, i, position mod 2] * (Ord(position in [0, 1]) * 2 - 1);
      coord[i, 1] := y + piece[current, i, 1 - position mod 2] * (Ord(position in [0, 3]) * 2 - 1);
    end;
  end;

{ draws a piece }
  procedure draw(current, x, y, position: Byte; visible: Boolean);
  begin
    coordinate(current, x, y, position);

    for i := 0 to 3 do begin
      GotoXY(coord[i, 0] * 2 + 1, coord[i, 1]);
      if visible then begin
        TextColor(color[current, 0]);
        TextBackground(color[current, 1]);
        Write('[]');
      end else begin
        TextBackground(Black);
        Write('  ');
      end;
    end;
  end;

{ check whether a piece can be placed in given position }
  function check(x2, y2, position2: Byte): Boolean;
  begin
    coordinate(current, x2, y2, position2);

    ok := True;
    for i := 0 to 3 do if area[coord[i, 0], coord[i, 1]] <> 0 then ok := False;

    if ok then begin
      x := x2;
      y := y2;
      position := position2;
    end;

    check := ok;
  end;

begin

  Randomize;
  TextColor(LightGray);
  TextBackground(Black);
  ClrScr;
  screen := WindMax;

  for i := 0 to width + 1 do for j := 1 to height + 1 do area[i, j] := 9 * Ord(not ((i in [1..width]) and (j in [1..height])));

  Window(1, 1, width * 2 + 4, height + 2);
  for i := 1 to (width + 2) * (height + 1) do Write('##');
  Window(3, 1, width * 2 + 2, height);
  ClrScr;
  Window(1, 1, Lo(screen), Hi(screen));

  Window(width * 2 + 7, 1, width * 2 + 25, 10);
  Writeln('Next');
  Writeln;
  Writeln;
  Writeln('Piece');
  Writeln('Line');
  Writeln('Level');
  Writeln('Score');
  Window(1, 1, Lo(screen), Hi(screen));

  played := 0;
  removed := 0;
  level := 1;
  score := 0;

  current := 9;
  next := 9;
  made := 0;

  repeat

    if current = 9 then begin
      if next = 9 then next := Random(7) + 1 else draw(next, width + 9, 1, 0, False);

      current := next;
      next := Random(7) + 1;
      x := width div 2;
      y := 1;
      position := 0;
      time := 1100 - level * 100;
      wait := time;

      Inc(played);
      Inc(score);
      if made = 25 then begin
        Inc(level);
        made := 0;
      end;

      draw(next, width + 9, 1, 0, True);

      Window(width * 2 + 15, 4, width * 2 + 25, 10);
      TextColor(LightGray);
      TextBackground(Black);
      Writeln(played:5);
      Writeln(removed:5);
      Writeln(level:5);
      Writeln(score:5);
      Window(1, 1, Lo(screen), Hi(screen));

      if not check(x, y, position) then begin
        GotoXY(width * 2 + 7, 10);
        Write('Game Over');
        Break;
      end;
    end;

    draw(current, x, y, position, True);
    GotoXY(1, 1);
    repeat Delay(1);
      Dec(wait);
    until KeyPressed or (wait = 0);
    draw(current, x, y, position, False);

    if KeyPressed then begin
      k := Ord(ReadKey);
      case k of
        75, 77: check(x + Ord(k = 77) * 2 - 1, y, position);
        72, 80: check(x, y, (position + Ord(k = 80) * 2 + 1) mod 4);
        32: begin
          time := 1;
          Inc(score);
        end;
      end;
    end;

    if wait = 0 then begin
      if not check(x, y + 1, position) then begin
        draw(current, x, y, position, True);
        for i := 0 to 3 do area[coord[i, 0], coord[i, 1]] := current;

        multi := 0;
        for j := 1 to height do begin
          ok := True;
          for i := 1 to width do if area[i, j] = 0 then ok := False;

          if ok then begin
            for j2 := j downto 2 do for i := 1 to width do area[i, j2] := area[i, j2 - 1];
            for i := 1 to width do area[i, 1] := 0;

            Inc(score, 10 + multi * 2);
            Inc(removed);
            Inc(multi);

            Window(3, 1, width * 2 + 2, height);
            TextBackground(Black);
            GotoXY(1, j);
            DelLine;
            GotoXY(1, 1);
            InsLine;
            Window(1, 1, Lo(screen), Hi(screen));
          end;

        end;
        if multi <> 0 then Inc(made);

        current := 9;
      end;
      wait := time;
    end;

  until k = 27;

  ReadKey;
  TextColor(LightGray);
  TextBackground(Black);
  ClrScr;

end.

스크린 샷

파스칼의 테트리스

(Linux의 경우 XTerm 창에서)

제어

  • Left – 왼쪽으로 이동
  • Right - 오른쪽으로 이동해라
  • Up – 반 시계 방향으로 회전
  • Down - 시계 방향으로 회전하세요
  • Space - 쓰러지 다
  • Esc – 종료

채점

  • 플레이 조각 – 1 포인트
  • 떨어진 조각 – 1 점
  • 완성 된 선 – 10 점
  • 여러 줄 – 승수 * 2 점

레벨은 1에서 시작하여 각 25 줄 완료 후에 증가합니다. (한 번에 완료된 여러 줄은 1로 계산됩니다.)

측정

bash-4.2$ sed '
s/{[^{}]*}//g                  # remove comments
s/^ *\| *$//g                  # trim leading and trailing spaces
/^$/d                          # remove empty lines
s/ *\([:=<>,;+*-]\+\) */\1/g   # no space around operators
' tetris.pas | wc -c
3697

17

자바 (스윙)

이것은 1989 년 부터 Nintendo (c) 의 최초의 역사적인 Game Boy 에디션 을 구현 한 것입니다 .

게임 보이 테트리스 닌텐도

게임 방법:

Z= 왼쪽으로 회전
X= 오른쪽으로 회전
Left= 왼쪽으로 이동
Right= 오른쪽으로
Down이동 = 아래로 이동 (느리게)
Up= 왼쪽으로 회전 (더 쉽게 사용하기 위해)
R= 게임 재설정

(골프 측면이 내 마음에 있기 때문에) 둘 이상의 클래스를 사용하지 않았습니다. 그러나 이제는 더 이상 골프를 칠 수 없습니다 ... 그러나 하나의 Font-File과 하나의 Image-File을 압축하고 Base64로 인코딩하여 하나의 클래스 파일에서만 사용할 수 있습니다.

이를 실행하려면 Java 코드를 IDE에 복사하고 시작하십시오. 추가 라이브러리 나 리소스가 필요하지 않습니다.

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.ZipInputStream;

import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.xml.bind.DatatypeConverter;

public class JTetris extends javax.swing.JFrame {
    static Image IMAGE;
    static Font FONT;

    static {
        ZipInputStream zipImage = new ZipInputStream(new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(
            "UEsDBBQAAAAIABmiWkRZ0oOouwAAAJYKAAAFAAAAYy5ibXBz8p3GxQAGZUCsAcQ3gFgCiBkZWMDiCkB5GBCA0iJyLAwl+3oYYpJsGK58u8NAKjBGAgyUgVG7QHYxKUGBAjDiBJUUQUhQAMQGA3xsFL3MMBMNRrpdEABhQxQLCiDYmHZBxCF2QdhAu8CyEHLk2cWkBJSH" +
            "xxcoCgyg5hsbAgWhbBgJUg9Wg24XWDGQHJF2QeOCUruQwEi0S0kRe5qHKCY2zQsgkv0ItAsIKC97EQnecGTYRQUwKNsbNAYAUEsBAj8AFAAAAAgAGaJaRFnSg6i7AAAAlgoAAAUAJAAAAAAAAAAgAAAAAAAAAGMuYm1wCgAgAAAAAAABABgAkJCiSyczzwGQkKJLJzPP" +
            "AZCQoksnM88BUEsFBgAAAAABAAEAVwAAAN4AAAAAAA=="
        )));

        ZipInputStream zipFont = new ZipInputStream(new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(
            "UEsDBBQAAAAIAEq5UEN0MGyWoggAAHgrAAAFAAAAYS50dGbklsvvC1EUx78zU+/3OxHiEuLd1jMlxKNR70cQCxumY9oOnU5Np6osdIGEDYmwZGElkYh/gI2FBUIsvFdILNgh8Rxft4d6RyxEYvq7PZ97es73nHNv8mthAOiJFiyotRtS0xMnum4Gej0HsNXx7erbI6+7" +
            "ks8CxqNiuVk48qTbVKB3BIy8XXLtbVv6x0cBkwuzS3SYrffvuL8MYGzJj3av3BfP4P4p8yeUA8cGxqWBSau4n+Lbu6tYET8AEoMBqIrtuzuPz33D/Uyg28VqUItgxtTrvRGAAoW4LK7BsHTnb5HAE9qB6E+PidGYhCTmYjP24AxuxTGYR98UpLEIdtsXP4zvxXfjO/HN" +
            "+Gp8Kg6p9O8//8ucRns93ZK6vqXfvBccXLuv7b/QH2LjVtyyYLUAdIfJaMmz6Gc83w3yd9OuSLS0WmcxRLj1xYJY/Gz9Wcyfr79XJ6aO3MEKTEAPWsDE988Y4yQM4RGAsEEeIWyiGzLCFsZjgXACY1EQ7oJeOCzclZnHhbthHs4Ld8cQPBPugT6GKdwT841+wr0wzFgl" +
            "3BtpwxHugxHGMeG+mGycEx6EicbdNhusYFqwYCQ4LTJmL2GDnBU20dfcK2xhjXlIOIFl5i3hLhhqjRLuiow1S7gbdlu+cHdMsh4L98DwRD/hntiTmCjcC8nEaeHe2Jq4IdwHmS7ThPtiUxdPeBDWd7nYZgPo33VANqg2Q69YitQ6Owo9Z4daZdd3uGp6etqMDfWqG6rV" +
            "dugF9Fa2qQlL+T9XLQ6aE9V6t1gv22EuqEQborDuROoX0b/87JPUJjeseUFFTUumdfhUHT71Y/hUHT71Y/jULyp6NWWrKLS3ub4d7lBBQeU2LFc6oBRU1fJK5IYVO6KoXVZL/fyyUhRV56ZSBUbUtETSCfyv5h5+6fWveh1+6ZVq2DWVr3vlSDW8qKQ6/fQU+UajkdQl" +
            "2MTHAj+s+sWW/dVSDG6kMpnMjGlzsqHLpne5Khv4Pj9TiyJ2mK9/DBQtR0KcdkQyCIupsue4lZpbS+WbqRnJdCr3USLvFdXOuu3s8CpFtcetlpphTW0P2Lzf5CS7Vd7dli0uWbStXIqQRYAqmgjhoYgSIiisg41Iexzs4H4V93WSS56ONKZhBjbQU6UnpG81bB0dSGwF" +
            "20gTsJTs66zFCFhjImk990XmlnVODgGjI6qxHr0Orfoz7T/P+66rTVqjprMrUJw3ifQX6lM76mRRJ31WJ4s66cczetRXjFbaazPfha9VeeLMKPA9x5zltB2FEqlKz3K9Zy+0NomdaCpDsQcfeSzTdxkxei5SfBVEo9bpglM59Po/v281HJfw+k/PVWe/oqcBW0+bp47H" +
            "HiPt82hLpB+dT89vum/oV7IzhZyETPD7s/7wUzk/elKi3CBl9GsGb38Osox05aR36SmzWs2XPIVFiOQMOSVE8Zu+nG9UnK80krQhiowr65twtdfVXeXR5PsMxqRpc5+7yJOKtDtZ09a356GiPXvgsm6JeaHubzsCOXkfTbmT3VrB5Q1mmbMEi0hl3TNM86VxFF2QMOXL" +
            "XVsuNnEF8mtAHquDQG7DxvVYSOFbbbfVQovG0NwOVV/kFKj3g4eVJZ5/3dALAzGUIl8/MMTDAvEBAC9oe9Hu/WJ/kD9s78f3gfdvpKrPs6H/A+1VkJtADAMnWXFAqEIVqjhw6KEfzQvan/Ud/cQmJdnRjoyzBLUCFAxeZ8aOHSfgk3aU+Kp6oFqX7/mn2s5L6KF+JPoC" +
            "8QOVOqdFAqiOpICIqxbYo82LiTEcccIZF7zjgyiD14JJ9MrUfmsUWAlrx3nybJXJ/b6v1/yIene+vjHV+OQrxNUsQ5XCpSYvtZBCXZeWD85FbA8CGE3TXXlkQwTIJkI1cvQrGmlXFAOiWYslTyXZJ1XX5JWTeT8IXbEKNaBKky2OmVbG17et7AMet8D6Vlh1kZq8sgdj" +
            "RckB7b9Tnx3ELVCmouKUtBHriQZczDu8dlk1i5w+SwGrFYp5mvtc03aE9NdGqP2rSNu3slZt6Ea4zTmuQc/Zcu1q0XEqj52KR/J9YIIwfN4gZOREc/Xdi+O4W6/anb3a9dGGxBoe1NRf8szqSdveBHqRE8SK4PtAbHr2cXFqh9rORFwMcgVACL6qgdi+WY9mzbjJ1agC" +
            "A2Uh8jgnE1wOHu0lvY4L+RGIZCzMHsnS+bwfbljlmyIz/X2Cyaf8t0jUNzue7CR+ZC32wD0kgyL537VkJY/XUtXl4/f3DWJDnotxhmJ67IyMMD2if9qO9ovOCYvrzqWCIf5Z+I4H4iCiODiKdFob37e6M40fO8Cyqtp4cxjXh5iNz2KXn84f5pI65vQ5nKrUWbPJ+fy7" +
            "nMF/ARx6tzdmc0q43dv7n3IcnZlxcHefezcFAJt3de7FX/bLHQWAEAaiuPe/srhiMcEncWDZMkUKwe/oJM+R+Ip1IqfPB1WCakTEbi7q6Jyb9sZ5oAgoYThNGjp24i9uhItx5j6DbyffdZC4XuWaY4gwHjnb1m/m0cxzUENOUK/VVs+f/S1qh79xC6dP/B+YfgEZgCsT" +
            "XRT0T4OPuu6oXXK5r1OxdrBO2xkcikrZz/l3jjozreH9LYpVi1WLVYtVi1VfjbZVGUfbqqNt1dG26mhbFaBB21blBJIgO+HzavBZNXWgKUQCsK8w58oQKQJ19BhR5mHMyUHYuOfWEHOEmL7+B49bDP3Dc/4Q0DW9aSsURmEYfqGx97+Po31eu4MFYdglBEHw47qIMAiD" +
            "YEEQBAuCYF1AGIZBGIRBGARB0MvBA3N8MMccc3wAkN1Zu2umvfeihkba+ueezEstLSxnogE8JMpk9thQDqGsVIWO8JSq0Bmeo/bwEjTUCV57Mn/ragPvUZkO1iWV+8u5rvDRl/NnRyv4CoqawndQ1Bx+0FhL+G0q1xX+oi6QDOAftTWBStAQqh0VUKtBvaTdDVBLAQI/" +
            "ABQAAAAIAEq5UEN0MGyWoggAAHgrAAAFACQAAAAAAAAAIAAAAAAAAABhLnR0ZgoAIAAAAAAAAQAYAABukYC8ys4BJXTvDjUzzwEldO8ONTPPAVBLBQYAAAAAAQABAFcAAADFCAAAAAA=")
        ));

        try {
            zipImage.getNextEntry();
            zipFont.getNextEntry();
            IMAGE = ImageIO.read(zipImage);
            FONT = Font.createFont(0, zipFont);
            FONT = FONT.deriveFont(20.0f);
        } catch (Exception e) {
            System.exit(1);
        }
    }

    int[] speeds = {887,820,753,686,619,552,468,368,284,184,167,150,133,117,100,100,83,83,66,66,50};
    int[] points = {40,100,300,1200};
    int[][][] stones = {
        {{0,1},{1,1},{2,1},{0,2}},      // L 0
        {{0,1},{1,1},{2,1},{3,1}},      // I 1
        {{0,1},{1,1},{2,1},{2,2}},      // J 2
        {{0,1},{1,1},{2,1},{1,2}},      // T 3
        {{0,1},{1,1},{1,2},{2,2}},      // Z 4
        {{1,1},{2,1},{0,2},{1,2}},      // S 5
        {{1,1},{2,1},{1,2},{2,2}}       // O 6
    };
    Color[] colors = {
        new Color(220, 246, 212), // light
        new Color(140, 190, 116), // green
        new Color(60, 98, 92),    // blue
        new Color(4, 30, 20)      // dark
    };
    int FREE = 0;
    int BORDER = 8;
    int DELTA = 20;
    int WIDTH = 10;
    int HEIGHT = 20;
    int SIZE = 24;

    int[][] field = new int[WIDTH][HEIGHT];
    int[][] stone;
    int deltaX;
    int deltaY;
    int curStone;
    int nextStone = new Random().nextInt(7);
    int score;
    int level;
    int lines;
    int steps;
    int speed = speeds[0];

    Timer tickTimer = new Timer();
    Image dbImage;
    Image staticImage;
    Graphics dbg;

    int anim = -1;
    boolean blink = false;
    ArrayList<Integer> removeLinesRows = new ArrayList<Integer>();
    boolean keyDown = true;
    boolean freezeKeys = false;

    private JTetris() {
        setDefaultCloseOperation(2);
        setResizable(false);
        setSize(475, 480);

        addPropertyChangeListener("isDirty", new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent e) {
                repaint();
            }
        });

        reset();

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                super.windowClosed(e);
                tickTimer.cancel();
            }
        });

        addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                keyDown = true;
            }

            @Override
            public void keyPressed(KeyEvent e) {
                if (freezeKeys) {
                    keyDown = false;
                    return;
                }

                switch (e.getKeyCode()) {
                    case 82: reset(); break;
                    case 37: updateField(-1, 0, false); break;
                    case 39: updateField(1, 0, false); break;
                    case 40: if (keyDown) updateField(0, 1, true); break;
                    case 38:
                    case 90: rotate(false); break;
                    case 88: rotate(true);
                }
            }
        });

        setVisible(true);
    }

    private void startTickTimer() {
        if (tickTimer != null) {
            tickTimer.cancel();
        }

        freezeKeys = false;

        tickTimer = new Timer();
        tickTimer.schedule(new TimerTask() {
            public void run() {
                updateField(0, 1, false);
            }
        }, 0, speed);
    }

    private void startRemoveLineTimer() {
        if (!removeLinesRows.isEmpty()) {
            anim = 7;
        }

        if (tickTimer != null) {
            tickTimer.cancel();
        }

        tickTimer = new Timer();
        tickTimer.schedule(new TimerTask() {
            public void run() {
                if (anim > 0) {
                    blink = !blink;
                    firePropertyChange("isDirty", false, true);
                }

                if (anim-- < 0) {
                    blink = false;
                    tickTimer.cancel();
                    if (removeLinesRows.size() > 0) {
                        int delta = 0;
                        for (int row : removeLinesRows) {
                            for (int r = row + delta++; r > 2; r--) {
                                for (int c = 0; c < WIDTH; c++) {
                                    field[c][r] = field[c][r - 1];
                                }
                            }
                        }

                        score += points[removeLinesRows.size() - 1] * (level + 1);
                        lines += removeLinesRows.size();
                        level = lines / 10;
                        speed = speeds[level > 20 ? 20 : level];
                        removeLinesRows.clear();
                    }

                    createNextStone();

                    startTickTimer();
                }
            }
        }, 50, 160);
    }

    public static void main(String[] a) {
        new JTetris();
    }

    @Override
    public void paint(Graphics g) {
        if (dbImage == null) {
            dbImage = createImage(getWidth(), getHeight());
            dbg = dbImage.getGraphics();
            dbg.setFont(FONT);

            staticImage = createImage(getWidth(), getHeight());
            Graphics sg = staticImage.getGraphics();
            sg.setFont(FONT);

            sg.setColor(colors[3]);
            sg.fillRect(0, 0, getWidth(), getHeight());

            sg.setColor(colors[0]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 70, 170, 58);
            sg.setColor(colors[2]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 73, 170, 52);
            sg.setColor(colors[0]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 95, 170, 27);
            sg.setColor(colors[2]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 98, 170, 3);

            sg.setColor(colors[1]);
            sg.fillRoundRect(40 + 13 * SIZE - 15, 40 + 13 * SIZE - 15, SIZE * 4 + 30, SIZE * 4 + 30, 20, 20);
            sg.setColor(colors[0]);
            sg.fillRoundRect(40 + 13 * SIZE - 12, 40 + 13 * SIZE - 12, SIZE * 4 + 24, SIZE * 4 + 24, 15, 15);

            sg.fillRoundRect(330 - 6, 60 - 6, 120 + 12, 25 + 12, 15, 15);
            sg.fillRoundRect(330 - 6, 155 - 6, 120 + 12, 60 + 12, 15, 15);
            sg.fillRoundRect(330 - 6, 235 - 6, 120 + 12, 60 + 12, 15, 15);
            sg.setColor(colors[2]);
            sg.fillRoundRect(40 + 13 * SIZE - 9, 40 + 13 * SIZE - 9, SIZE * 4 + 18, SIZE * 4 + 18, 15, 15);

            sg.fillRoundRect(330 - 3, 60 - 3, 120 + 6, 25 + 6, 15, 15);
            sg.fillRoundRect(330 - 3, 155 - 3, 120 + 6, 60 + 6, 15, 15);
            sg.fillRoundRect(330 - 3, 235 - 3, 120 + 6, 60 + 6, 15, 15);
            sg.setColor(colors[3]);
            sg.fillRoundRect(40 + 13 * SIZE - 6, 40 + 13 * SIZE - 6, SIZE * 4 + 12, SIZE * 4 + 12, 15, 15);

            sg.setColor(colors[0]);
            sg.fillRoundRect(330, 60, 120, 25, 5, 5);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 40, 3, (HEIGHT - 2) * SIZE);
            sg.fillRoundRect(40 + 13 * SIZE - 3, 40 + 13 * SIZE - 3, SIZE * 4 + 6, SIZE * 4 + 6, 5, 5);
            sg.fillRoundRect(330, 155, 120, 60, 15, 15);
            sg.fillRoundRect(330, 235, 120, 60, 15, 15);

            sg.setColor(colors[3]);
            sg.drawString("SCORE", 340, 80);
            sg.drawString("LEVEL", 340, 180);
            sg.drawString("LINES", 340, 260);

            for (int r = 0; r < (HEIGHT - 2) * 24 / 18; r++) {
                sg.drawImage(IMAGE, 16, 40 + r * 18, 16 + SIZE, 58 + r * 18, 8 * SIZE, 0, 9 * SIZE, 18, this);
                sg.drawImage(IMAGE, 40 + WIDTH * SIZE, 40 + r * 18, 40 + WIDTH * SIZE + SIZE, 58 + r * 18, 8 * SIZE, 0, 9 * SIZE, 18, this);
            }
        }

        dbg.drawImage(staticImage, 0, 0, this);

        dbg.setColor(colors[3]);
        dbg.drawString(String.format("%6s", score > 999999 ? 999999 : score), 320, 120);
        dbg.drawString(String.format("%4s", level > 20 ? 20 : level), 340, 205);
        dbg.drawString(String.format("%4s", lines > 9999 ? 9999 : lines), 340, 285);

        for (int x = 0; x < 4; x++) {
            drawField(dbg, stones[nextStone][x][0] + 13, stones[nextStone][x][1] + 13, nextStone + 1);
        }

        for (int r = 2; r < HEIGHT; r++) {
            for (int c = 0; c < WIDTH; c++) {
                drawField(dbg, c, r - 2, field[c][r]);
            }
        }

        if (blink) {
            if (anim == 0)  {
                dbg.setColor(colors[0]);
            } else {
                dbg.setColor(colors[1]);
            }

            for (int row : removeLinesRows) {
                dbg.fillRect(40, 40 + (row - 2) * SIZE, SIZE * WIDTH, SIZE);
            }
        }

        g.drawImage(dbImage, 0, 0, this);
    }

    private void drawField(Graphics g, int x, int y, int idx) {
        if (idx > DELTA) {
            idx -= DELTA;
        }

        g.drawImage(IMAGE, 40 + x * SIZE, 40 + y * SIZE, SIZE + 40 + x * SIZE, SIZE + 40 + y * SIZE, idx * SIZE, 0, (idx + 1) * SIZE, SIZE, this);
    }

    private void createNextStone() {
        curStone = nextStone;
        stone = stones[curStone];
        deltaX = (WIDTH + 1) / 2 - 2;
        deltaY = 1;

        if (!isMoveable(0, 0)) {
            tickTimer.cancel();
            JOptionPane.showMessageDialog(this, "Game over! Press OK for a new game.");
            reset();
            return;
        }

        updateField(0, 0, false);
        nextStone = new Random().nextInt(7);
    }

    private void reset() {
        score = 0;
        steps = 0;
        lines = 0;
        level = 0;
        speed = speeds[0];

        for (int r = 0; r < HEIGHT; r++) {
            for (int c = 0; c < WIDTH; c++) {
                field[c][r] = FREE;
            }
        }

        createNextStone();
        startTickTimer();
    }

    private void updateField(int hor, int ver, boolean player) {
        if (isMoveable(hor, ver)) {
            if (player) {
                steps++;
            }

            for (int x = 0; x < 4; x++) {
                field[stone[x][0] + deltaX][stone[x][1] + deltaY] = FREE;
            }

            deltaX += hor;
            deltaY += ver;

            for (int x = 0; x < 4; x++) {
                field[stone[x][0] + deltaX][stone[x][1] + deltaY] = curStone + 1;
            }

            firePropertyChange("isDirty", false, true);
        } else if (ver == 1) {
            freezeKeys = true;
            fixStone();
            removeLines();
        }
    }

    private void removeLines() {
        outer:
        for (int r = HEIGHT - 1; r >= 0; r--) {
            for (int c = 0; c < WIDTH; c++) {
                if (field[c][r] == FREE) {
                    continue outer;
                }
            }

            removeLinesRows.add(r);
        }

        startRemoveLineTimer();
    }

    private void fixStone() {
        score += steps;
        steps = 0;

        for (int x = 0; x < 4; x++) {
            field[stone[x][0] + deltaX][stone[x][1] + deltaY] = curStone + 1 + DELTA;
        }
    }

    private boolean isMoveable(int hor, int ver) {
        for (int x = 0; x < 4; x++) {
            int c = stone[x][0] + deltaX + hor;
            int r = stone[x][1] + deltaY + ver;

            if (c < 0 || c >= WIDTH || r < 0 || r >= HEIGHT) {
                return false;
            }

            if (field[c][r] > DELTA) {
                return false;
            }
        }

        return true;
    }

    private void rotate(boolean r) {
        if (curStone == 6) { // O
            return;
        }

        synchronized (field) {
            int[][] o = new int[4][2];

            for (int x = 0; x < 4; x++) {
                o[x] = new int[] { r ? 3 - stone[x][1] : stone[x][1], r ? stone[x][0] : 3 - stone[x][0] };
            }

            if (canRotate(o)) {
                for (int x = 0; x < 4; x++) {
                    field[stone[x][0] + deltaX][stone[x][1] + deltaY] = FREE;
                }

                stone = o;

                for (int x = 0; x < 4; x++) {
                    field[stone[x][0] + deltaX][stone[x][1] + deltaY] = curStone + 1;
                }

                firePropertyChange("isDirty", false, true);
            }
        }
    }

    private boolean canRotate(int[][] o) {
        for (int x = 0; x < 4; x++) {
            if (o[x][0] + deltaX < 0 ||
                o[x][0] + deltaX >= WIDTH ||
                o[x][1] + deltaY >= HEIGHT ||
                field[o[x][0] + deltaX][o[x][1] + deltaY] > DELTA) {
                return false;
            }
        }

        return true;
    }
}

할 일 :

  • 높은 수준의 빠른 돌 (완료!)
  • 원본 라인 애니메이션 제거 (완료!)
  • 라인에 대한 올바른 포인트 (완료!)
  • 새로운 돌로 내려가는 것을 방해 (완료!)
  • 최고 점수 화면
  • 원래 회전 동작
  • 레벨 선택 메뉴
  • 게임 오버 화면
  • 오디오

의견 환영합니다 :)


2
나는 정말로 이것을 정말로 좋아합니다. 유용성에 대한 한 가지 제안 : 사용자가 블록을 떨어 뜨리기 위해 ↓를 누르고 있으면 블록이 착륙 한 후이 키를 프로그래밍 방식으로 '릴리스'합니다 (즉, 사용자는 키를 놓은 다음 다시 누르면 다음 블록이 가속됩니다). .
primo

큰! 나는 이미 그것을 다루는 방법에 대해 생각하고 있었다. 이것은 좋은 방법이 될 것입니다! 감사!
bobbel

1
+1 그의 첫번째 Nintendo GameBoy와 함께 8 살짜리처럼 비명을 질렀습니다!
WallyWest

끔찍한 Java 코드가 끔찍한 지 레트로인지 확실하지 않습니다. 어쨌든 +1. ;)
TwoThe

한 번만 사용되는 클래스의 경우 코드를 가져 오지 않고 코드에서 정규화 된 이름을 사용하여 코드를 줄이십시오. 예를 들어을 제거 import java.awt.event.KeyAdapter;하고로 변경 new KeyAdapter하십시오 new java.awt.event.KeyAdapter.
벤 골드버그

8

루아-2876

터미널의 테트리스는 대부분의 유닉스 시스템, 순수한 루아에서 작동하며 추가 라이브러리가 필요하지 않습니다.

제어 : wasd 또는 hjkl, w / k to drop, s / j to rotation, ad / hl to move

여러 줄을 제거 할 때마다 파괴 된 줄의 제곱을 얻습니다.

이것은 가장 가능성이 높은 골프 솔루션은 아니지만 어쨌든 약간의 골프를하기로 결정했습니다. 줄 바꿈은 80 열의 텍스트에 적합하기 때문에 문자 수에 포함시키지 않았습니다.

math.randomseed(os.time())if arg[1]then local function s(e)local e=io.popen(
"sleep "..e)e:read"*a"return e:close()end local l={{0,1,1,0,1,1},{-1,1,-1,0,1,0}
,{-1,0,1,0,1,1},{-1,0,0,1,1,0},{-1,1,0,1,1,0},{-1,0,0,1,1,1},{-1,0,1,0,2,0}}
function T(t,e,o)if o>1 then return T(-e,t,o-1)end return t,e end local t 
function fit(i,n,r,o)if n<1 or n>10 or r<1 or t[r][n]~=0 then return end for e=1
,6,2 do local e,o=T(l[i][e],l[i][e+1],o)e,o=e+n,o+r if e<1 or e>10 or o<1 or((t[
o]or{})[e]or 0)~=0 then return end end return i end function put(e,r,i,n)t[i][r]
=e for o=1,6,2 do local n,o=T(l[e][o],l[e][o+1],n)n,o=n+r,o+i;(t[o]or{})[n]=e 
end end while 1 do os.execute"clear"io.write("\27[0;37m+----------++----+\r\n"..
("|          ||    |\r\n"):rep(2)..
"|          |+----+\r\n|          |Score:\r\n"..("|          |\r\n"):rep(16)..
"+----------+\27[20F\27[C")t={}for e=1,20 do t[e]={}for o=1,10 do t[e][o]=0 end 
end local e,o,f,r,a,c,n=20,5,0,math.random(7),math.random(7),0,1 while 1 do f=f+
1 s(.1)local d=io.open(arg[1],"rb")or os.exit(0,io.write"\27[49;39m",io.stdout:
flush(),os.execute"clear")local i=d:read(1)while i do if i=="a"or i=="h"then if 
fit(r,o-1,e,n)then o=o-1 end elseif i=="d"or i=="l"then if fit(r,o+1,e,n)then o=
o+1 end elseif i=="w"or i=="k"then while fit(r,o,e-1,n)do e=e-1 end elseif i==
"s"or i=="j"then if fit(r,o,e,n+1)then n=n%4+1 end end i=d:read(1)end d:close()d
=io.open(arg[1],"w")d:write()d:close()if f%(math.max(1,10-math.floor(c/20)))==0 
then if fit(r,o,e-1,n)then e=e-1 else put(r,o,e,n)r,e,o,a,n=a,20,5,math.random(7
),1 if not fit(r,o,e,n)then break end local e=1 local o=0 while e<21 do local n=
1 for o=1,10 do n=n*t[e][o]end if n~=0 then table.remove(t,e)local e={}for o=1,
10 do e[o]=0 end table.insert(t,e)o=o+1 else e=e+1 end end c=c+o*o end end put(r
,o,e,n)for e=20,1,-1 do for o=1,10 do io.write("\27[3"..t[e][o].."m"..(t[e][o]
==0 and" "or"#"))end io.write"\27[E\27[C"end io.write("\27[20F\27[13C\27[3",a,
"m")for e=1,0,-1 do for n=-1,2 do local o for t=1,6,2 do if l[a][t]==n and l[a][
t+1]==e then io.write"#"o=e end end if e==0 and n==0 then io.write"#"o=e end if 
not o then io.write" "end end io.write"\27[4D\27[B"end io.write(
"\27[2B\27[D\27[49;37m",c,"\27[4F\27[C")t[e][o]=0 for i=1,6,2 do local r,n=T(l[r
][i],l[r][i+1],n)r,n=r+o,n+e;(t[n]or{})[r]=0 end end io.write"\27[49;39m"io.
stdout:flush()os.execute"clear"io.write
"\aGame over. Will automatically start new game in 5 seconds. Use ^C to exit"s(5
)io.stdout:flush()end else local e=os.tmpname()local o=io.open(e,"w")local n=
newproxy(true)getmetatable(n).__gc=function()os.execute"stty -raw echo"end os.
execute"stty raw -echo"local t=""i=0 while arg[i]do t=arg[i].." "..t i=i-1 end 
os.execute(t..e.." &")while 1 do o:close()local t=io.read(1)o=io.open(e,"a")o:
write(t)if t=="\3"then o:close()os.remove(e)os.execute"stty -raw echo"
getmetatable(n).__gc=nil os.execute"sleep 0.1"os.exit()end end end

터미널의 테트리스


3
ungolfed 버전이 있습니까?
nyuszika7h

7
@ nyuszika7h 무슨 뜻인가요? 그는 그것을 사용하여 그렇게 썼습니다 cat.
Camilo Martin

1
@CamiloMartin 무엇입니까 cat, 나는 butterfiles를 사용하고 있습니다
mniip

3
@mniip 아 그래! 좋은 Ol ' C-x M-c M-butterfly...
카밀로 마틴

6

매스 매 티카

이 코드는 Xiangdong Wen이 Mathematica로 작성했으며 실제로 웹 브라우저에서 재생할 수 있습니다. Shape Descender (화살표 키를 시작하려면 그래픽을 클릭하십시오). 아래는 스크린 샷과 전체 코드입니다-이 게임의 완전한 웹 앱을 위해서는 상당히 무섭습니다.

여기에 이미지 설명을 입력하십시오

암호

allBlocks = {{{{1, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 
      0}}, {{0, 1, 1, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 
      0}}, {{0, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 
      0}}, {{0, 1, 0, 0}, {0, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 
      0}}}, {{{0, 2, 0, 0}, {2, 2, 2, 0}, {0, 0, 0, 0}, {0, 0, 0, 
      0}}, {{0, 2, 0, 0}, {0, 2, 2, 0}, {0, 2, 0, 0}, {0, 0, 0, 
      0}}, {{0, 0, 0, 0}, {2, 2, 2, 0}, {0, 2, 0, 0}, {0, 0, 0, 
      0}}, {{0, 2, 0, 0}, {2, 2, 0, 0}, {0, 2, 0, 0}, {0, 0, 0, 
      0}}}, {{{0, 0, 3, 0}, {3, 3, 3, 0}, {0, 0, 0, 0}, {0, 0, 0, 
      0}}, {{0, 3, 0, 0}, {0, 3, 0, 0}, {0, 3, 3, 0}, {0, 0, 0, 
      0}}, {{0, 0, 0, 0}, {3, 3, 3, 0}, {3, 0, 0, 0}, {0, 0, 0, 
      0}}, {{3, 3, 0, 0}, {0, 3, 0, 0}, {0, 3, 0, 0}, {0, 0, 0, 0}}},
   {{{0, 0, 0, 0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}, {{0, 0, 
      0, 0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}, {{0, 0, 0, 
      0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}, {{0, 0, 0, 
      0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}}, {{{0, 0, 0, 
      0}, {5, 5, 5, 5}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 5, 
      0}, {0, 0, 5, 0}, {0, 0, 5, 0}, {0, 0, 5, 0}}, {{0, 0, 0, 
      0}, {5, 5, 5, 5}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 5, 
      0}, {0, 0, 5, 0}, {0, 0, 5, 0}, {0, 0, 5, 0}}}, {{{6, 6, 0, 
      0}, {0, 6, 6, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 6, 
      0}, {0, 6, 6, 0}, {0, 6, 0, 0}, {0, 0, 0, 0}}, {{6, 6, 0, 
      0}, {0, 6, 6, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 6, 
      0}, {0, 6, 6, 0}, {0, 6, 0, 0}, {0, 0, 0, 0}}}, {{{0, 7, 7, 
      0}, {7, 7, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 7, 0, 
      0}, {0, 7, 7, 0}, {0, 0, 7, 0}, {0, 0, 0, 0}}, {{0, 7, 7, 
      0}, {7, 7, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 7, 0, 
      0}, {0, 7, 7, 0}, {0, 0, 7, 0}, {0, 0, 0, 0}}}};
smallBoard = 
  Table[If[i == 1 || j == 1 || i == 6 || j == 6, 9, 0], {i, 1, 6}, {j,
     1, 6}];

color[v_] := 
  Switch[v, 0, Black, 1, Yellow, 2, Blue, 3, Magenta, 4, Cyan, 5, Red,
    6, Orange, 7, Green, 9, Gray, _, Gray];
showSquare[{i_, j_}, v_] := {color[v], 
   Rectangle[{i + 0.05, j + 0.05}, {i + 0.95, j + 0.95}]};
showHintBlock[n_, p_, x_, y_] := 
  Table[showSquare[{i + x, j + y}, allBlocks[[n, p, i, j]]], {i, 1, 
    4}, {j, 1, 4}];


initBoard[
   Hold[board_, num_]] := {board = 
    Table[If[i <= 3 || j <= 3 || i >= 14 || j >= 24, 9, 0], {i, 1, 
      16}, {j, 1, 28}], 
   Table[board[[i, 24]] = 0; board[[i, 25]] = 0; 
    board[[i, 26]] = 0, {i, 7, 10}]; 
   Table[board[[i, 25]] = 0, {i, 1, 16}]; board[[6, 25]] = 9, 
   board[[11, 25]] = 9,
   Table[If[Mod[j + i, 2] == 0, board[[j, i + 3]] = 9], {i, 1, 
     num}, {j, 4, 13}]};

bMoveTo[n_, p_, x_, y_, Hold[board_]] := 
  Total[Table[
     allBlocks[[n, p, i, j]]* board[[i + x, j + y]], {i, 1, 4}, {j, 1,
       4}], 2] == 0;
bGameOver[n_, p_, Hold[board_]] := 
  Not[bMoveTo[n, p, 6, 22, Hold[board]]];
bLeft[n_, p_, x_, y_, Hold[board_]] := 
  bMoveTo[n, p, x - 1, y, Hold[board]];
bRight[n_, p_, x_, y_, Hold[board_]] := 
  bMoveTo[n, p, x + 1, y, Hold[board]];
bDown[n_, p_, x_, y_, Hold[board_]] := 
  bMoveTo[n, p, x, y - 1, Hold[board]];
bRotateAnticlock[n_, p_, x_, y_, Hold[board_]] := 
 Module[{tp}, tp = p - 1; If[tp == 0, tp = 4]; 
  bMoveTo[n, tp, x, y, Hold[board]]]; 
bRotateClockwise[n_, p_, x_, y_, Hold[board_]] := 
 Module[{tp}, tp = p + 1; If[tp == 5, tp = 1]; 
  bMoveTo[n, tp, x, y, Hold[board]]];
showBoard[Hold[board_]] := 
  Table[showSquare[{i, j}, board[[i, j]]], {i, 3, 14}, {j, 3, 25}];
showDropBlock[n_, p_, x_, y_] := 
  Table[If[allBlocks[[n, p, i, j]] != 0, 
    showSquare[{i + x, j + y}, allBlocks[[n, p, i, j]]], Black], {i, 
    1, 4}, {j, 1, 4}];


updateBoard[n_, p_, x_, y_, 
   Hold[board_, score_]] := {Table[
    board[[i + x, j + y]] += allBlocks[[n, p, i, j]], {i, 1, 4}, {j, 
     1, 4}]; score += 
    10 + 100*(2^
         Sum[updateLine[i, Hold[board]], {i, y + 4, y + 1, -1}] - 
        1)};
updateLine[line_, Hold[board_]] := 
  If[line <= 23 && 
    line >= 4 && (Apply[And, 
       Table[board[[i, line]] != 0, {i, 4, 13}]]) == True, 
   Table[board[[i, j]] = board[[i, j + 1]], {i, 4, 13}, {j, line, 
     22}]; Table[board[[i, 23]] = 0, {i, 4, 13}]; 1, 0];
newGame[Hold[n_, p_, x_, y_, board_, nextItem_, gameOver_, pause_, 
    num_]] := {initBoard[
    Hold[board, num]]; {n, p, x, y} = {nextItem, 2, 6, 22}; 
   nextItem = RandomInteger[6] + 1; gameOver = False; pause = False};
newBlock[Hold[n_, p_, x_, y_, board_, score_, gameOver_, nextItem_, 
    pause_]] := {If[! pause && ! gameOver, 
    updateBoard[n, p, x, y, Hold[board, score]]; 
    gameOver = bGameOver[nextItem, 2, Hold[board]]; 
    If[gameOver == False, {n, p, x, y} = {nextItem, 2, 6, 22}; 
     nextItem = RandomInteger[6] + 1]]};


shapeDescend[Hold[level_, state_, num_]] := 
  DynamicModule[{score = 0, gameOver = False, pause = False, 
    board = Table[
      If[i <= 3 || j <= 3 || i >= 14 || j >= 24, 0, 0], {i, 1, 
       16}, {j, 1, 28}], n, p, x, y, nextItem}, 
   SeedRandom[level*10 + num]; nextItem = RandomInteger[6] + 1; 
   newGame[Hold[n, p, x, y, board, nextItem, gameOver, pause, num]];
   EventHandler[
    Graphics[{Text[Style["Level", Blue, Italic, 24], {18, 12}], 
      Text[Style["Score", Blue, Italic, 24], {18, 9}], 
      Table[showSquare[{i + 16, j + 13}, smallBoard[[i, j]]], {i, 1, 
        6}, {j, 1, 6}], 
      Dynamic[Refresh[
        Switch[state, 1, 
         newGame[Hold[n, p, x, y, board, nextItem, gameOver, pause, 
           num]]; state = 3,
         2, pause = True,
         3, pause = False], UpdateInterval -> Infinity, 
        TrackedSymbols -> {state}]; 
       Refresh[If[! pause && ! gameOver && 
          bDown[n, p, x, y, Hold[board]], y--, 
         newBlock[
          Hold[n, p, x, y, board, score, gameOver, nextItem, pause]]],
         UpdateInterval -> 3./level, TrackedSymbols -> {}];
       Join[{{Text[
           Style[ToString[level], Green, Italic, 24], {20, 11}], 
          Text[Style[ToString[score], Green, Italic, 24], {20, 8}]}}, 
        showBoard[Hold[board]], showDropBlock[n, p, x, y], 
        showHintBlock[nextItem, 2, 17, 14]], 
       TrackedSymbols -> {x, y, n, p, level}]}, Background -> Black, 
     ImageSize -> {400, 400}], {"LeftArrowKeyDown" :> 
      If[! pause && ! gameOver && bLeft[n, p, x, y, Hold[board]], x--],
     "RightArrowKeyDown" :> 
      If[! pause && ! gameOver && bRight[n, p, x, y, Hold[board]], 
       x++], "DownArrowKeyDown" :> (If[! pause && ! gameOver && 
         bRotateClockwise[n, p, x, y, Hold[board]], p++; 
        If[p == 5, p = 1]]), 
     "UpArrowKeyDown" :> (If[! pause && ! gameOver && 
         bRotateAnticlock[n, p, x, y, Hold[board]], p--; 
        If[p == 0, p = 4]]), {"KeyDown", " "} :> 
      If[! pause && ! gameOver && bDown[n, p, x, y, Hold[board]], y--,
        newBlock[
        Hold[n, p, x, y, board, score, gameOver, nextItem, pause]]],
     "EscapeKeyDown" :> {newGame[
        Hold[n, p, x, y, board, nextItem, gameOver, pause, num]]}}]];

Manipulate[
 shapeDescend[Hold[level, state, initLine]],
 {{level, 5}, 1, 9, 1},
 {{initLine, 3, "height of bottom level"}, 0, 9, 1},
 {{state, 3, ""}, {1 -> "new game", 2 -> "pause", 3 -> "continue"}},
 ContentSize -> {480, 450}, SynchronousUpdating -> False, 
 SaveDefinitions -> True, Alignment -> Center, TrackedSymbols -> {}]

4

나는 고등학교에서 수업에 지루하게 앉아있는 동안이 yeaaaaars 전에 썼다. 아마도 원래 프로그래머는 대괄호를 블록으로 사용하여 첫 번째 버전의 Tetris를 썼으며, 다시 시도하고 다시 만들만큼 간단 해 보였습니까? GUI에 대해 전혀 몰랐기 때문에 구식 콘솔 프로그램을 만들었습니다. 이것은 C ++과 그 성가신 적절한 프로그래밍 방법을 배우기 전에 돌아 왔으므로 코드가 약간 지저분 할 수 있습니다. 나는 거의 그것을 날개를 쳤다.

TKlone

피스가 한 방향 (시계 방향)으로 만 회전하는 것을 제외하고는 챌린지의 모든 요구 사항을 충족합니다. WASD를 사용하여 재생하면 W가 회전합니다. 전체 소스 코드와 exe는 여기에서 찾을 수 있습니다 : http://sourceforge.net/projects/tklone/files/tklone/tklone-v1.0/

tetris.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include "defines.h"

int array[FIELD_H][FIELD_W];    //field array
int pieceCoords[2];             //current piece coordinates
int x, y;                       //looping variables
int thisPiece, nextPiece;       //current pieces
double score;                   //the players current score
int newpiece=1;                 //the newpiece flag
int init_h=1;                   /*starting coordinates of piece*/
int init_w=4;                   /*these are obsolete variables*/
int h_offset, w_offset;         //piece position offsets from start
int rtTop, rtBot, rtLeft, rtRight;  //rotation thresholds for collision
int b1h, b1w, b2h, b2w, b3h, b3w;   //block draw offsets
int rotation;                   /*0=normal view
                                  1=90 degrees clockwise
                                  2=upside-down
                                  3=270 degrees clockwise */

clock_t speed;          //time before piece falls (in milliseconds)
clock_t gravity;        //temp variable for speed

//clock_t fps;
int piececount;         //number of pieces that have fallen
int oldpiececount;
int level;


void GameInit(void);
int GameStart(void);
int GameMain(void);
void newPiece(void);

void scrollbar(void);           //scrolling marquee bar
void drawField(void);           //graphics rendering function
void setPiece(int piece);       //this sets the individual piece offsets
void drawPiece(void);           //this sets the blocks in the array
void initArray(void);           //this initializes the array
void getMoves(char move);       //input function
void rotate(int coll);          //loops from 1 to 4
int collisionCheck(int side);   //checks to see if piece will collide
int loseGame(void);             //checks to see if game has ended
void eraseLastMove(void);       //clears the array of the last move
void setLastMove(void);         //sets the piece in place for the next piece
void lineClear(void);           //clears filled lines and tabulates the score
void predict(int undo);         //predicts the next piece rotation for collision
//void delay(int duration);

int main()
{
restart:
    GameInit();
    //some test blocks
    /*for(x=FIELD_H;x>3;x--)        //quick lose pattern
        array[x][4]=1;*/

    nextPiece=rand()%7;     //generate a piece to use
    drawField();            //draw a blank field. should be a title screen
    printf("                             / Welcome to TETRIS! \\"); //29 spaces and title

    getch();            //wait to start the game

    while(1)    //main game loop
    {
        if(newpiece==1)         //check to see if a new piece is required
            newPiece();

        //if(gravity<=clock())
            gravity=speed+clock();  //get the end time
        while(gravity>clock())      //count to the end time
        {
            if(kbhit())
            {
                getMoves(getch());  //get input
                if(newpiece==1)     //if piece reaches bottom...
                    break;          //...skip by redraw to save some cycles
                else
                {
                    eraseLastMove();    //clear the last move from the array
                    drawPiece();        //set the piece in the array
                    drawField();        //refresh the screen
                    scrollbar();        //draw the scrollbar
                }
            }
        }
        if(newpiece==0)     /*minor speed optimization*/
            getMoves('s');  //force piece down a line
        eraseLastMove();    //clear the last move from the array
        drawPiece();        //set the piece in the array
        drawField();        //refresh the screen
        scrollbar();        //draw the scrollbar
    }
    return 0;
}

void GameInit()
{
    srand((unsigned)time(NULL));    //seed the random number genreator...
    initArray();                    //...and initialize the array
    //reset some variables
    score=0;
    piececount=-1;      //set to -1 because of newpiece loop on start
    oldpiececount=0;
    speed=1000;         //starting speed of piece
    level=1;
}

void newPiece()
{
    piececount++;       //another piece has fallen
    setLastMove();      //set the last move in place
    lineClear();        //clear any full lines

    newpiece=0;         //reset the newpiece flag
    rotation=0;         //reset the piece rotation
    h_offset=0;         //reset the...
    w_offset=0;         //...piece coordinates
    gravity=0;          //reset the gravity

    if(piececount-oldpiececount==10)    //if 10 more pieces have fallen
        if(speed!=200)                  //200 is the lowest speed it can go
        {
            oldpiececount=piececount;   //set current number of fallen pieces
            speed-=100;                 //make the speed faster
            level++;                    //increment the level counter
        }

    thisPiece=nextPiece;//get the current piece
    nextPiece=rand()%7; //pick the next piece

    setPiece(thisPiece);//get piece data
    if(loseGame())      //did player lose the game?
    {
        drawField();    //refresh the screen
        printf("You lose! Final score: %.0f. Press any key to begin a new game.",score);
        getch();
        newpiece=1;     //reset the newpiece flag /*add more initialization here*/
        //goto restart; //return to start of the program
        exit(0);        //quit the game for now
    }
    drawPiece();        //set the piece in the array
    drawField();        //refresh the screen
    scrollbar();        //draw the scrollbar
}

void scrollbar()
{
    //printf("================================================================================");

    printf("Score: %.0f\t\t      ",score);  //print the score (6 spaces)
    if(nextPiece==5||nextPiece==6)          //print a space if the piece name is short
        printf(" ");

    printf("Next piece: ");
    if(nextPiece==0)            //print the name of the next piece
        printf("straight");
    else if(nextPiece==1)
        printf("t-shaped");
    else if(nextPiece==2)
        printf("s-shaped");
    else if(nextPiece==3)
        printf("z-shaped");
    else if(nextPiece==4)
        printf("l-shaped");
    else if(nextPiece==5)
        printf("back-l");
    else if(nextPiece==6)
        printf("square");

    printf("\t\t\tLevel %i",level); //print the level
}

void drawField()
{
    system("cls");      //clear the screen

    for(x=0;x<FIELD_H;x++)
    {
        printf("                              |");  //thirty spaces

        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==1||array[x][y]==2)
                printf("[]");
            else
                printf("  ");

        printf("|                              ");  //thirty spaces
    }

    //bottom message bar
    //scrollbar();
}

void drawPiece()
{
    /* This sets the initial location of the piece with
       movement offset. Coordinates are based on the
       center of the piece. */
    pieceCoords[0]=init_h+h_offset; //set height position
    pieceCoords[1]=init_w+w_offset; //set width position

    //mark the coordinates to draw the piece
    array[(pieceCoords[0])][(pieceCoords[1])]=2;        //center piece
    array[(pieceCoords[0]+b1h)][(pieceCoords[1]+b1w)]=2;    //other...
    array[(pieceCoords[0]+b2h)][(pieceCoords[1]+b2w)]=2;    //...three...
    array[(pieceCoords[0]+b3h)][(pieceCoords[1]+b3w)]=2;    //...pieces
}

void initArray()
{
    for(x=0;x<FIELD_H;x++)
        for(y=0;y<FIELD_W;y++)
            array[x][y]=0;
        //array equals: 0 for empty
        //              1 for full
        //              2 for moving piece
}

void getMoves(char move)
{
    switch(move)
    {
        case 'a':
        case 'A':
            if(collisionCheck(1)==0)    //if the piece doesnt hit something...
                w_offset--;             //...move the piece
            break;
        case 'd':
        case 'D':
            if(collisionCheck(2)==0)
                w_offset++;
            break;
        case 'w':
        case 'W':
            if(collisionCheck(0)==0)
            {
                rotate(0);              //rotate the piece and...
                setPiece(thisPiece);    //...get new piece data
            }
            break;
        case 's':
        case 'S':
            if(collisionCheck(3)==0)
                h_offset++;
            else                        //if the piece hits the bottom...
                newpiece=1;             //...set the newpiece flag
            break;
        default:
            break;
    }
}

void rotate(int coll)
{
    if(coll==0)     //rotate clockwise
    {
        if(rotation==3)
            rotation=0;
        else
            rotation++;
    }

    if(coll==1)     //rotate counterclockwise
    {
        if(rotation==0)
            rotation=3;
        else
            rotation--;
    }
}

int collisionCheck(int side)
{
    if(side==0) //check rotation collision
    {
        predict(0);

        /* For the first part, we check to see if the
           rotation will collide with the sides. */
        for(x=0;x<rtTop;x++)    //check top side collision
        {
            if(pieceCoords[0]-x==0) //if piece minus top offset hits the top
            {
                predict(1);
                return 1;   //collision detected!
            }
        }

        for(x=0;x<rtLeft;x++)   //check left side collision
        {
            if(pieceCoords[1]-x==0)
            {
                predict(1);
                return 1;
            }
        }

        for(x=0;x<rtRight;x++)  //check right side collision
        {
            if(pieceCoords[1]+x==(FIELD_W-1))
            {
                predict(1);
                return 1;
            }
        }

        for(x=0;x<rtBot;x++)    //check bottom side collision
        {
            if(pieceCoords[0]+x==(FIELD_H-1))
            {
                predict(1);
                return 1;
            }
        }

        /* Then we check to see if any of the
           blocks will hit other blocks on the field. */
        if(array[(pieceCoords[0]+b1h)][(pieceCoords[1]+b1w)]==1
    ||array[(pieceCoords[0]+b2h)][(pieceCoords[1]+b2w)]==1
    ||array[(pieceCoords[0]+b3h)][(pieceCoords[1]+b3w)]==1)
        {
            predict(1);
            return 1;
        }

        predict(1);
        return 0;
    }

    /* Here, we scan the array and check to see whether
       any of the blocks in the current piece are
       adjacent to the field sides or another block. */
    for(x=0;x<FIELD_H;x++)          //scan the array
        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==2)      //if array location contains a block
            {
                if(side==1) //check left side
                {
                    if(y==0||array[x][y-1]==1)  //if piece is next to border or block...
                        return 1;   //collision detected!
                }

                else if(side==2)    //check right side
                {
                    if(y==FIELD_W-1||array[x][y+1]==1)
                        return 1;
                }

                else if(side==3)    //check bottom
                {
                    if(x==FIELD_H-1||array[x+1][y]==1)
                        return 1;
                }
            }

    return 0;
}

int loseGame()      /*need to modify the conditions of losing*/
{
    if(array[init_h][init_w]==1
||array[(init_h+b1h)][(init_w+b1w)]==1
||array[(init_h+b2h)][(init_w+b2w)]==1
||array[(init_h+b3h)][(init_w+b3w)]==1)
        return 1;

    else
        return 0;
}

void eraseLastMove()    /*need to optimize this*/
{
    for(x=0;x<FIELD_H;x++)      //scan the array
        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==2)      //if the spot is filled...
                array[x][y]=0;      //...clear the block
}

void setLastMove()      /*also need to optimize this*/
{
    for(x=0;x<FIELD_H;x++)      //scan the array
        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==2)      //if the spot is filled...
                array[x][y]=1;      //...set the block
}

void lineClear()
{
    int x2, y2; //new looping variables
    int empty;  //is the row empty?
    int lines=0;//number of lines cleared

    for(x=0;x<FIELD_H;x++)      //scan the rows
    {
        empty=0;                //reset the empty flag on each row

        for(y=0;y<FIELD_W;y++)  //scan the blocks in each row
            if(array[x][y]==0)  //if the row isn't full...
            {
                empty=1;        //...set the empty flag and...
                break;          //...scan the next row
            }

        if(!empty)
        {
            for(y=0;y<FIELD_W;y++)      //rescan the filled row...
                array[x][y]=0;          //...and clear the blocks

            for(x2=x-1;x2>=0;x2--)      //scan rows from the bottom up starting above cleared row
                for(y2=0;y2<FIELD_W;y2++)   //scan each block in the row
                    if(array[x2][y2]==1)
                    {
                        array[x2][y2]=0;    //clear the block
                        array[x2+1][y2]=1;  //fill in the block below it
                    }

            lines++;            //a line has been cleared
        }
    }

    //tabulate the scores
    if(lines==1)
        score+=10;
    else if(lines==2)
        score+=25;
    else if(lines==3)
        score+=50;
    else if(lines==4)   /* TETRIS! */
        score+=100;
}

void predict(int undo)
{
    if(undo==0)
    {
        rotate(0);              //get next rotation...
        setPiece(thisPiece);    //...and next piece data
    }

    if(undo==1)
    {
        rotate(1);              //un-rotate
        setPiece(thisPiece);    //reset the piece
    }
}

/*void delay(int duration)
{
    clock_t done;

    done = duration + clock();
    while(done>clock())
        ;   //sit and wait
}*/

setPiece.c

#include "defines.h"

extern int rtTop, rtBot, rtLeft, rtRight;   //rotation thresholds for collision
extern int b1h, b1w, b2h, b2w, b3h, b3w;    //block draw offsets
extern int rotation;                    /*0=normal view
                                          1=90 degrees clockwise
                                          2=upside-down
                                          3=270 degrees clockwise */

void setPiece(int piece)
{
    if(piece==0)
    {
        switch(rotation)
        {
            case 0:     //piece looks identical rotated upside-down
            case 2:
                /* The rotation thresholds dictate how far away the piece
                   needs to be from the field edge for the next rotation. */
                rtTop=1;
                rtBot=2;
                rtLeft=0;
                rtRight=0;

                /* These are the coordinates of the
                   blocks relative to the center block. */
                b1h=-1;
                b1w=0;
                b2h=1;
                b2w=0;
                b3h=2;
                b3w=0;
                break;
            case 1:     //piece looks identical side to side
            case 3:
                rtTop=0;
                rtBot=0;
                rtLeft=1;
                rtRight=2;

                b1h=0;
                b1w=-1;
                b2h=0;
                b2w=1;
                b3h=0;
                b3w=2;
                break;
        }
    }
        //draw blue straight piece

    //snip other pieces

    else if(piece==6)
    {
        switch(rotation)
        {
            case 0:     //a cube looks identical when rotated 90 degrees
            case 1:
            case 2:
            case 3:
                rtTop=0;    //piece doesn't rotate so no thresholds needed
                rtBot=0;
                rtLeft=0;
                rtRight=0;

                b1h=-1;
                b1w=0;
                b2h=-1;
                b2w=1;
                b3h=0;
                b3w=1;
                break;
        }
    }
        //draw square piece
}

안녕하세요! PPCG에 오신 것을 환영합니다. 최대 게시물 크기에 비해 너무 큰 경우가 아니라면 답변 본문에 소스 코드를 실제로 포함시키는 것이 좋습니다. 그럼에도 불구하고, 일부 예시 발췌를 제공하는 것이 좋습니다.
Jonathan Van Matre

2
코드가 추가되었습니다. setPiece.c는 약간 축약되었고 define.h는 표시되지 않습니다. 원래 코드는 링크에서 찾을 수 있습니다.
Bigbio2002

1
대박! 공유해 주셔서 감사합니다.
Jonathan Van Matre

1

나는 자바 스크립트 버전 https://marchingband.github.io/tetris/를 만들었습니다.

여기에 이미지 설명을 입력하십시오

<!DOCTYPE html>
<html>
  <body>
      <p>q and w rotate</p>
      <p id='score'>0</p>
      <div id='board' style='position:absolute;top:80px' ></div>
  </body>
</html>
  <script>
    var pieceIndex=0
    nextPiece=()=>{
      pieceIndex=pieceIndex<pieces.length-1?pieceIndex+1:0
      return pieces[pieceIndex]
    }
    const pieces=[
    [
        {x:0,y:-1},
        {x:0,y:0},
        {x:1,y:0},
        {x:-1,y:0},
      ],
      [
        {x:0,y:-2},
        {x:0,y:-1},
        {x:0,y:0},
        {x:0,y:1},
      ],
      [
        {x:1,y:0},
        {x:0,y:0},
        {x:2,y:0},
        {x:0,y:-1},
      ],
      [
        {x:0,y:0},
        {x:1,y:0},
        {x:1,y:1},
        {x:0,y:1}
      ],
      [
        {x:0,y:0},
        {x:-1,y:0},
        {x:0,y:1},
        {x:1,y:1}
      ],
    ]
    const speeds =[1000,800,600,400,300,250,200,150,100,90,80,70,60,50,40,30,20]
    const boardWidth = 10
    const boardHeight = 20
    const pixelSize   = 10
    const pixelBorder = 0
    const boardBorder = 5
    
    var score = 0
    var level = 0
    var fallingBits = []

    const b = document.getElementById('board')
     piece = {
      position:{
        x:boardWidth/2,
        y:1,
      },
      bits:[
        {x:0,y:-1},
        {x:0,y:0},
        {x:1,y:0},
        {x:-1,y:0},
      ],
      reset(){
        piece.position={x:boardWidth/2,y:1}
        piece.bits=nextPiece()
        !piece.coordinates().every((b)=>dom[b.x][b.y]?dom[b.x][b.y].style.background=='red':true) && endGame()
      },
      coordinates(){
        return piece.bits.map((b)=>{
          return {x:b.x+piece.position.x,y:b.y+piece.position.y}
        })
      }
    }
    const dom = []
    for(var x=0;x<boardWidth;x++){
      var row = []
      for(var y=0;y<boardHeight;y++){
        var cell = document.createElement('div')
        cell.style.cssText='outline:thin solid;height:'+pixelSize+'px;width:'+pixelSize+'px;background:red;position:absolute'
        cell.style.left= (x*(pixelSize+pixelBorder)+boardBorder) + 'px'
        cell.style.top = (y*(pixelSize+pixelBorder)+boardBorder) + 'px'
        row.push(cell)
        b.appendChild(cell)
      }
      dom.push(row)
    }
    renderPiece=(color)=>{
      piece.coordinates().forEach((b)=>dom[b.x][b.y] && (dom[b.x][b.y].style.background=color))
    }
    drop=()=>{
      piece.coordinates().every((p)=>p.y+1<boardHeight) && piece.coordinates().every((p)=>dom[p.x][p.y+1].style.background!='blue') ?
        movePiece(0,1) : freeze()
    }
    strafe=(d)=>{
      piece.coordinates().every((p)=>p.x+d<boardWidth && p.x+d >=0) && piece.coordinates().every((p)=>dom[p.x+d][p.y].style.background!='blue') ?
        movePiece(d,0) : null
    }
    freeze=()=>{
      piece.coordinates().forEach((p)=>dom[p.x][p.y].style.background='blue')
      lookForRows()
      piece.reset()
    }
    lookForRows=()=>{
      [...dom[0].keys()].filter((i)=>dom.every((c)=>c[i].style.background=='blue')).forEach((r)=>{
        removeLine(r)
        compressAbove(r)
        addPoint()
      })
    }
    compressAbove=(r)=>{
      // dom.reduce((acc,col,x)=>{return acc.concat(col.map((b,y)=>{return {x:x,y:y}}).filter((b,y)=>y<r && dom[b.x][b.y].style.background=='blue'))},[]).forEach((b)=>dom[b.x][b.y].style.background='red').forEach((b)=>dom[b.x][b.y+1].style.background='blue')
      var bits = []
      dom.forEach((x,i)=>{
        x.forEach((y,j)=>{
          if(j<=r && dom[i][j].style.background=='blue'){bits.push({x:i,y:j})}
        })
      })
      bits.forEach((b)=>{
        dom[b.x][b.y].style.background='red'
      })
      bits.forEach((b)=>{
        dom[b.x][b.y+1].style.background='blue'
      })
    }
    removeLine=(r)=>{
      dom.forEach((x)=>x[r].style.background='red')
    }
    changePiece=(x,y)=>{
      piece.position.x += x
      piece.position.y += y
    }
    movePiece=(x,y)=>{
        renderPiece('red')
        changePiece(x,y)
        renderPiece('yellow')
    }
    turn=(d)=>{
      var newBits = []
      for(let i=0;i<piece.bits.length;i++){
        var x = piece.bits[i].x
        var y = piece.bits[i].y
        var bit={}
        if(d==1){
          bit.x=-y
          bit.y=x
        }else{
          bit.x=y
          bit.y=-x
        }
        newBits.push(bit)
      }
      if(newBits.every((p)=>p.x+piece.position.x<boardWidth && p.x+piece.position.x>=0 && p.y+piece.position.y<boardHeight && dom[p.x+piece.position.x][p.y+piece.position.y].style.background!='blue')){
        renderPiece('red')
        piece.bits=newBits
        renderPiece('yellow')
      }
    }
    addPoint=()=>{
      score++
      document.getElementById('score').innerText=score
      score-level*10 > 10 && levelUp()
    }
    levelUp=()=>{
      clearInterval(timer)
      level++
      timer = window.setInterval(drop,speeds[level])
    }
    endGame=()=>{
      clearInterval(timer)
      dom.forEach((row)=>{
        row.forEach((bit)=>{
          bit.style.background=='red' && (bit.style.background='black')
        })
      })
    }
    document.onkeydown = function(e) {
    switch (e.keyCode) {
        case 37:strafe(-1);break
        case 87:turn(1);break
        case 81:turn(-1);break
        case 39:strafe(1);break
        case 40:drop();break
    }
};
  var timer = window.setInterval(drop,1000)
  </script>
</html>


좋은데 불행히도, 모든 조각이 구덩이의 바닥까지 떨어 파이어 폭스, 작은 버그가 dom[p.x][p.y+1].style.background반환 blue none repeat scroll 0% 0%, 그래서 동일하지 않습니다 blue. 귀하의 솔루션은 유효하지만 Firefox와 Chrome에서 모두 제대로 작동하도록 .style.background→ 변경하는 것이 좋습니다 .style.backgroundColor.
manatwork
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.