1+ import java .awt .Color ;
2+ import java .awt .Graphics ;
3+ import java .awt .Point ;
4+ import java .awt .event .KeyEvent ;
5+ import java .awt .event .KeyListener ;
6+ import java .util .ArrayList ;
7+ import java .util .Collections ;
8+
9+ import javax .swing .JFrame ;
10+ import javax .swing .JPanel ;
11+
12+ public class Tetris extends JPanel {
13+
14+ private static final long serialVersionUID = -8715353373678321308L ;
15+
16+ private final Point [][][] Tetraminos = {
17+ // I-Piece
18+ {
19+ { new Point (0 , 1 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (3 , 1 ) },
20+ { new Point (1 , 0 ), new Point (1 , 1 ), new Point (1 , 2 ), new Point (1 , 3 ) },
21+ { new Point (0 , 1 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (3 , 1 ) },
22+ { new Point (1 , 0 ), new Point (1 , 1 ), new Point (1 , 2 ), new Point (1 , 3 ) }
23+ },
24+
25+ // J-Piece
26+ {
27+ { new Point (0 , 1 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (2 , 0 ) },
28+ { new Point (1 , 0 ), new Point (1 , 1 ), new Point (1 , 2 ), new Point (2 , 2 ) },
29+ { new Point (0 , 1 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (0 , 2 ) },
30+ { new Point (1 , 0 ), new Point (1 , 1 ), new Point (1 , 2 ), new Point (0 , 0 ) }
31+ },
32+
33+ // L-Piece
34+ {
35+ { new Point (0 , 1 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (2 , 2 ) },
36+ { new Point (1 , 0 ), new Point (1 , 1 ), new Point (1 , 2 ), new Point (0 , 2 ) },
37+ { new Point (0 , 1 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (0 , 0 ) },
38+ { new Point (1 , 0 ), new Point (1 , 1 ), new Point (1 , 2 ), new Point (2 , 0 ) }
39+ },
40+
41+ // O-Piece
42+ {
43+ { new Point (0 , 0 ), new Point (0 , 1 ), new Point (1 , 0 ), new Point (1 , 1 ) },
44+ { new Point (0 , 0 ), new Point (0 , 1 ), new Point (1 , 0 ), new Point (1 , 1 ) },
45+ { new Point (0 , 0 ), new Point (0 , 1 ), new Point (1 , 0 ), new Point (1 , 1 ) },
46+ { new Point (0 , 0 ), new Point (0 , 1 ), new Point (1 , 0 ), new Point (1 , 1 ) }
47+ },
48+
49+ // S-Piece
50+ {
51+ { new Point (1 , 0 ), new Point (2 , 0 ), new Point (0 , 1 ), new Point (1 , 1 ) },
52+ { new Point (0 , 0 ), new Point (0 , 1 ), new Point (1 , 1 ), new Point (1 , 2 ) },
53+ { new Point (1 , 0 ), new Point (2 , 0 ), new Point (0 , 1 ), new Point (1 , 1 ) },
54+ { new Point (0 , 0 ), new Point (0 , 1 ), new Point (1 , 1 ), new Point (1 , 2 ) }
55+ },
56+
57+ // T-Piece
58+ {
59+ { new Point (1 , 0 ), new Point (0 , 1 ), new Point (1 , 1 ), new Point (2 , 1 ) },
60+ { new Point (1 , 0 ), new Point (0 , 1 ), new Point (1 , 1 ), new Point (1 , 2 ) },
61+ { new Point (0 , 1 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (1 , 2 ) },
62+ { new Point (1 , 0 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (1 , 2 ) }
63+ },
64+
65+ // Z-Piece
66+ {
67+ { new Point (0 , 0 ), new Point (1 , 0 ), new Point (1 , 1 ), new Point (2 , 1 ) },
68+ { new Point (1 , 0 ), new Point (0 , 1 ), new Point (1 , 1 ), new Point (0 , 2 ) },
69+ { new Point (0 , 0 ), new Point (1 , 0 ), new Point (1 , 1 ), new Point (2 , 1 ) },
70+ { new Point (1 , 0 ), new Point (0 , 1 ), new Point (1 , 1 ), new Point (0 , 2 ) }
71+ }
72+ };
73+
74+ private final Color [] tetraminoColors = {
75+ Color .cyan , Color .blue , Color .orange , Color .yellow , Color .green , Color .pink , Color .red
76+ };
77+
78+ private Point pieceOrigin ;
79+ private int currentPiece ;
80+ private int rotation ;
81+ private ArrayList <Integer > nextPieces = new ArrayList <Integer >();
82+
83+ private long score ;
84+ private Color [][] well ;
85+
86+ // Creates a border around the well and initializes the dropping piece
87+ private void init () {
88+ well = new Color [12 ][24 ];
89+ for (int i = 0 ; i < 12 ; i ++) {
90+ for (int j = 0 ; j < 23 ; j ++) {
91+ if (i == 0 || i == 11 || j == 22 ) {
92+ well [i ][j ] = Color .GRAY ;
93+ } else {
94+ well [i ][j ] = Color .BLACK ;
95+ }
96+ }
97+ }
98+ newPiece ();
99+ }
100+
101+ // Put a new, random piece into the dropping position
102+ public void newPiece () {
103+ pieceOrigin = new Point (5 , 2 );
104+ rotation = 0 ;
105+ if (nextPieces .isEmpty ()) {
106+ Collections .addAll (nextPieces , 0 , 1 , 2 , 3 , 4 , 5 , 6 );
107+ Collections .shuffle (nextPieces );
108+ }
109+ currentPiece = nextPieces .get (0 );
110+ nextPieces .remove (0 );
111+ }
112+
113+ // Collision test for the dropping piece
114+ private boolean collidesAt (int x , int y , int rotation ) {
115+ for (Point p : Tetraminos [currentPiece ][rotation ]) {
116+ if (well [p .x + x ][p .y + y ] != Color .BLACK ) {
117+ return true ;
118+ }
119+ }
120+ return false ;
121+ }
122+
123+ // Rotate the piece clockwise or counterclockwise
124+ public void rotate (int i ) {
125+ int newRotation = (rotation + i ) % 4 ;
126+ if (newRotation < 0 ) {
127+ newRotation = 3 ;
128+ }
129+ if (!collidesAt (pieceOrigin .x , pieceOrigin .y , newRotation )) {
130+ rotation = newRotation ;
131+ }
132+ repaint ();
133+ }
134+
135+ // Move the piece left or right
136+ public void move (int i ) {
137+ if (!collidesAt (pieceOrigin .x + i , pieceOrigin .y , rotation )) {
138+ pieceOrigin .x += i ;
139+ }
140+ repaint ();
141+ }
142+
143+ // Drops the piece one line or fixes it to the well if it can't drop
144+ public void dropDown () {
145+ if (!collidesAt (pieceOrigin .x , pieceOrigin .y + 1 , rotation )) {
146+ pieceOrigin .y += 1 ;
147+ } else {
148+ fixToWell ();
149+ }
150+ repaint ();
151+ }
152+
153+ // Make the dropping piece part of the well, so it is available for
154+ // collision detection.
155+ public void fixToWell () {
156+ for (Point p : Tetraminos [currentPiece ][rotation ]) {
157+ well [pieceOrigin .x + p .x ][pieceOrigin .y + p .y ] = tetraminoColors [currentPiece ];
158+ }
159+ clearRows ();
160+ newPiece ();
161+ }
162+
163+ public void deleteRow (int row ) {
164+ for (int j = row -1 ; j > 0 ; j --) {
165+ for (int i = 1 ; i < 11 ; i ++) {
166+ well [i ][j +1 ] = well [i ][j ];
167+ }
168+ }
169+ }
170+
171+ // Clear completed rows from the field and award score according to
172+ // the number of simultaneously cleared rows.
173+ public void clearRows () {
174+ boolean gap ;
175+ int numClears = 0 ;
176+
177+ for (int j = 21 ; j > 0 ; j --) {
178+ gap = false ;
179+ for (int i = 1 ; i < 11 ; i ++) {
180+ if (well [i ][j ] == Color .BLACK ) {
181+ gap = true ;
182+ break ;
183+ }
184+ }
185+ if (!gap ) {
186+ deleteRow (j );
187+ j += 1 ;
188+ numClears += 1 ;
189+ }
190+ }
191+
192+ switch (numClears ) {
193+ case 1 :
194+ score += 100 ;
195+ break ;
196+ case 2 :
197+ score += 300 ;
198+ break ;
199+ case 3 :
200+ score += 500 ;
201+ break ;
202+ case 4 :
203+ score += 800 ;
204+ break ;
205+ }
206+ }
207+
208+ // Draw the falling piece
209+ private void drawPiece (Graphics g ) {
210+ g .setColor (tetraminoColors [currentPiece ]);
211+ for (Point p : Tetraminos [currentPiece ][rotation ]) {
212+ g .fillRect ((p .x + pieceOrigin .x ) * 26 ,
213+ (p .y + pieceOrigin .y ) * 26 ,
214+ 25 , 25 );
215+ }
216+ }
217+
218+ @ Override
219+ public void paintComponent (Graphics g )
220+ {
221+ // Paint the well
222+ g .fillRect (0 , 0 , 26 *12 , 26 *23 );
223+ for (int i = 0 ; i < 12 ; i ++) {
224+ for (int j = 0 ; j < 23 ; j ++) {
225+ g .setColor (well [i ][j ]);
226+ g .fillRect (26 *i , 26 *j , 25 , 25 );
227+ }
228+ }
229+
230+ // Display the score
231+ g .setColor (Color .WHITE );
232+ g .drawString ("" + score , 19 *12 , 25 );
233+
234+ // Draw the currently falling piece
235+ drawPiece (g );
236+ }
237+
238+ public static void main (String [] args ) {
239+ JFrame f = new JFrame ("Tetris" );
240+ f .setDefaultCloseOperation (JFrame .EXIT_ON_CLOSE );
241+ f .setSize (12 *26 +10 , 26 *23 +25 );
242+ f .setVisible (true );
243+
244+ final Tetris game = new Tetris ();
245+ game .init ();
246+ f .add (game );
247+
248+ // Keyboard controls
249+ f .addKeyListener (new KeyListener () {
250+ public void keyTyped (KeyEvent e ) {
251+ }
252+
253+ public void keyPressed (KeyEvent e ) {
254+ switch (e .getKeyCode ()) {
255+ case KeyEvent .VK_UP :
256+ game .rotate (-1 );
257+ break ;
258+ case KeyEvent .VK_DOWN :
259+ game .rotate (+1 );
260+ break ;
261+ case KeyEvent .VK_LEFT :
262+ game .move (-1 );
263+ break ;
264+ case KeyEvent .VK_RIGHT :
265+ game .move (+1 );
266+ break ;
267+ case KeyEvent .VK_SPACE :
268+ game .dropDown ();
269+ game .score += 1 ;
270+ break ;
271+ }
272+ }
273+
274+ public void keyReleased (KeyEvent e ) {
275+ }
276+ });
277+
278+ // Make the falling piece drop every second
279+ new Thread () {
280+ @ Override public void run () {
281+ while (true ) {
282+ try {
283+ Thread .sleep (1000 );
284+ game .dropDown ();
285+ } catch ( InterruptedException e ) {}
286+ }
287+ }
288+ }.start ();
289+ }
290+ }
0 commit comments