├── .gitignore ├── src ├── PlayerSkeleton.java ├── TFrame.java ├── State.java └── TLabel.java └── readme.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | -------------------------------------------------------------------------------- /src/PlayerSkeleton.java: -------------------------------------------------------------------------------- 1 | 2 | public class PlayerSkeleton { 3 | 4 | //implement this function to have a working system 5 | public int pickMove(State s, int[][] legalMoves) { 6 | 7 | return 0; 8 | } 9 | 10 | public static void main(String[] args) { 11 | State s = new State(); 12 | new TFrame(s); 13 | PlayerSkeleton p = new PlayerSkeleton(); 14 | while(!s.hasLost()) { 15 | s.makeMove(p.pickMove(s,s.legalMoves())); 16 | s.draw(); 17 | s.drawNext(0,0); 18 | try { 19 | Thread.sleep(300); 20 | } catch (InterruptedException e) { 21 | e.printStackTrace(); 22 | } 23 | } 24 | System.out.println("You have completed "+s.getRowsCleared()+" rows."); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | This is a basic Tetris simulation. 2 | 3 | Files: 4 | State - tetris simulation 5 | TFrame - frame that draws the board 6 | TLabel - drawing library 7 | PlayerSkeleton - setup for implementing a player 8 | 9 | 10 | State: 11 | This is the tetris simulation. It keeps track of the state and allows you to 12 | make moves. The board state is stored in field (a double array of integers) and 13 | is accessed by getField(). Zeros denote an empty square. Other values denote 14 | the turn on which that square was placed. NextPiece (accessed by getNextPiece) 15 | contains the ID (0-6) of the piece you are about to play. 16 | 17 | Moves are defined by two numbers: the SLOT, the leftmost column of the piece and 18 | the ORIENT, the orientation of the piece. Legalmoves gives an nx2 int array 19 | containing the n legal moves. A move can be made by specifying the two 20 | parameters as either 2 ints, an int array of length 2, or a single int 21 | specifying the row in the legalMoves array corresponding to the appropriate move. 22 | 23 | It also keeps track of the number of lines cleared - accessed by getRowsCleared(). 24 | 25 | draw() draws the board. 26 | drawNext() draws the next piece above the board 27 | clearNext() clears the drawing of the next piece so it can be drawn in a different 28 | slot/orientation 29 | 30 | 31 | 32 | 33 | TFrame: 34 | This extends JFrame and is instantiated to draw a state. 35 | It can save the current drawing to a .png file. 36 | The main function allows you to play a game manually using the arrow keys. 37 | 38 | 39 | 40 | TLabel: 41 | This is a drawing library. 42 | 43 | 44 | 45 | PlayerSkeleton: 46 | An example of how to implement a player. 47 | The main function plays a game automatically (with visualization). 48 | 49 | 50 | xx 51 | xx 52 | 53 | 54 | xxxx 55 | 56 | X 57 | XXX 58 | 59 | X 60 | XXX 61 | 62 | x 63 | xxx 64 | 65 | xx 66 | xx 67 | 68 | xx 69 | xx 70 | -------------------------------------------------------------------------------- /src/TFrame.java: -------------------------------------------------------------------------------- 1 | import java.awt.Graphics2D; 2 | import java.awt.event.KeyEvent; 3 | import java.awt.event.KeyListener; 4 | import java.awt.image.BufferedImage; 5 | import java.io.File; 6 | import java.io.IOException; 7 | 8 | import javax.imageio.ImageIO; 9 | import javax.swing.JFrame; 10 | 11 | 12 | 13 | public class TFrame extends JFrame implements KeyListener{ 14 | private static final long serialVersionUID = 1L; 15 | public TLabel label = new TLabel(300,700); 16 | public State s; 17 | 18 | public int orient, slot; 19 | 20 | public static final int MANUAL = 0; 21 | public static final int NONE = 1; 22 | 23 | public int mode = MANUAL; 24 | 25 | //constructor 26 | public TFrame (State s){ 27 | this.s = s; 28 | s.label = label; 29 | setResizable(false); 30 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows when this is closed 31 | setTitle("Tetris BKW"); 32 | setContentPane(label.draw); 33 | pack(); 34 | label.BORDER = .05; 35 | label.setXscale(0, State.COLS); 36 | label.setYscale(0, State.ROWS+5); 37 | this.addKeyListener(this); //may be unnecessary (not certain) 38 | setVisible(true); 39 | } 40 | 41 | //switches which state is attached to this TFrame 42 | public void bindState(State s) { 43 | if(s!= null) s.label = null; 44 | this.s = s; 45 | s.label = label; 46 | } 47 | 48 | /// 49 | /// ADDED BY DON (AKA Pimp Masta) 1/22/09 50 | /// 51 | public TFrame (){ 52 | s.label = label; 53 | setResizable(false); 54 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows when this is closed 55 | setTitle("Eric Whitman's Tetris Simulator"); 56 | setContentPane(label.draw); 57 | pack(); 58 | label.BORDER = .05; 59 | label.setXscale(0, State.COLS); 60 | label.setYscale(0, State.ROWS+5); 61 | this.addKeyListener(this); //may be unnecessary (not certain) 62 | setVisible(true); 63 | } 64 | 65 | public void keyPressed(KeyEvent e) { 66 | switch(mode) { 67 | case(MANUAL): { 68 | switch(e.getKeyCode()) { 69 | case(KeyEvent.VK_RIGHT): { 70 | if(slot < State.COLS-State.pWidth[s.nextPiece][orient]) slot++; 71 | s.clearNext(); 72 | s.drawNext(slot, orient); 73 | break; 74 | } 75 | case(KeyEvent.VK_LEFT): { 76 | if(slot > 0) slot--; 77 | s.clearNext(); 78 | s.drawNext(slot, orient); 79 | break; 80 | } 81 | case(KeyEvent.VK_UP): { 82 | orient++; 83 | if(orient%State.pOrients[s.nextPiece]==0) orient = 0; 84 | if(slot > State.COLS-State.pWidth[s.nextPiece][orient]) 85 | slot = State.COLS-State.pWidth[s.nextPiece][orient]; 86 | s.clearNext(); 87 | s.drawNext(slot, orient); 88 | break; 89 | } 90 | case(KeyEvent.VK_DOWN): { 91 | if(!s.makeMove(orient, slot)) mode = NONE; 92 | if(orient >= State.pOrients[s.nextPiece]) orient = 0; 93 | if(slot > State.COLS-State.pWidth[s.nextPiece][orient]) 94 | slot = State.COLS-State.pWidth[s.nextPiece][orient]; 95 | 96 | s.draw(); 97 | if(mode == NONE) { 98 | label.text(State.COLS/2.0, State.ROWS/2.0, "You Lose"); 99 | } 100 | s.clearNext(); 101 | s.drawNext(slot, orient); 102 | break; 103 | } 104 | default: 105 | break; 106 | } 107 | } 108 | case(NONE): break; 109 | default: 110 | System.out.println("unknown mode"); 111 | break; 112 | } 113 | 114 | 115 | 116 | 117 | } 118 | 119 | 120 | public void keyReleased(KeyEvent e) { 121 | } 122 | 123 | 124 | public void keyTyped(KeyEvent e) { 125 | 126 | } 127 | 128 | public void save(String filename) { 129 | File file = new File(filename); 130 | String suffix = filename.substring(filename.lastIndexOf('.') + 1); 131 | 132 | 133 | BufferedImage bImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); 134 | Graphics2D graphic = (Graphics2D)bImage.getGraphics(); 135 | paint(graphic); 136 | graphic.drawImage(bImage, 0, 0, null); 137 | // png files 138 | if (suffix.toLowerCase().equals("png")) { 139 | try { ImageIO.write(bImage, suffix, file); } 140 | catch (IOException e) { e.printStackTrace(); } 141 | } 142 | else System.out.println("unknown extension"); 143 | } 144 | 145 | public static void main(String[] args) { 146 | State s = new State(); 147 | TFrame t = new TFrame(s); 148 | s.draw(); 149 | s.drawNext(0,0); 150 | //t.save("picture.png"); 151 | 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/State.java: -------------------------------------------------------------------------------- 1 | import java.awt.Color; 2 | 3 | 4 | 5 | 6 | public class State { 7 | public static final int COLS = 10; 8 | public static final int ROWS = 21; 9 | public static final int N_PIECES = 7; 10 | 11 | 12 | 13 | public boolean lost = false; 14 | 15 | 16 | 17 | 18 | 19 | public TLabel label; 20 | 21 | //current turn 22 | private int turn = 0; 23 | private int cleared = 0; 24 | 25 | //each square in the grid - int means empty - other values mean the turn it was placed 26 | private int[][] field = new int[ROWS][COLS]; 27 | //top row+1 of each column 28 | //0 means empty 29 | private int[] top = new int[COLS]; 30 | 31 | 32 | //number of next piece 33 | protected int nextPiece; 34 | 35 | 36 | 37 | //all legal moves - first index is piece type - then a list of 2-length arrays 38 | protected static int[][][] legalMoves = new int[N_PIECES][][]; 39 | 40 | //indices for legalMoves 41 | public static final int ORIENT = 0; 42 | public static final int SLOT = 1; 43 | 44 | //possible orientations for a given piece type 45 | protected static int[] pOrients = {1,2,4,4,4,2,2}; 46 | 47 | //the next several arrays define the piece vocabulary in detail 48 | //width of the pieces [piece ID][orientation] 49 | protected static int[][] pWidth = { 50 | {2}, 51 | {1,4}, 52 | {2,3,2,3}, 53 | {2,3,2,3}, 54 | {2,3,2,3}, 55 | {3,2}, 56 | {3,2} 57 | }; 58 | //height of the pieces [piece ID][orientation] 59 | private static int[][] pHeight = { 60 | {2}, 61 | {4,1}, 62 | {3,2,3,2}, 63 | {3,2,3,2}, 64 | {3,2,3,2}, 65 | {2,3}, 66 | {2,3} 67 | }; 68 | private static int[][][] pBottom = { 69 | {{0,0}}, 70 | {{0},{0,0,0,0}}, 71 | {{0,0},{0,1,1},{2,0},{0,0,0}}, 72 | {{0,0},{0,0,0},{0,2},{1,1,0}}, 73 | {{0,1},{1,0,1},{1,0},{0,0,0}}, 74 | {{0,0,1},{1,0}}, 75 | {{1,0,0},{0,1}} 76 | }; 77 | private static int[][][] pTop = { 78 | {{2,2}}, 79 | {{4},{1,1,1,1}}, 80 | {{3,1},{2,2,2},{3,3},{1,1,2}}, 81 | {{1,3},{2,1,1},{3,3},{2,2,2}}, 82 | {{3,2},{2,2,2},{2,3},{1,2,1}}, 83 | {{1,2,2},{3,2}}, 84 | {{2,2,1},{2,3}} 85 | }; 86 | 87 | //initialize legalMoves 88 | { 89 | //for each piece type 90 | for(int i = 0; i < N_PIECES; i++) { 91 | //figure number of legal moves 92 | int n = 0; 93 | for(int j = 0; j < pOrients[i]; j++) { 94 | //number of locations in this orientation 95 | n += COLS+1-pWidth[i][j]; 96 | } 97 | //allocate space 98 | legalMoves[i] = new int[n][2]; 99 | //for each orientation 100 | n = 0; 101 | for(int j = 0; j < pOrients[i]; j++) { 102 | //for each slot 103 | for(int k = 0; k < COLS+1-pWidth[i][j];k++) { 104 | legalMoves[i][n][ORIENT] = j; 105 | legalMoves[i][n][SLOT] = k; 106 | n++; 107 | } 108 | } 109 | } 110 | 111 | } 112 | 113 | 114 | public int[][] getField() { 115 | return field; 116 | } 117 | 118 | public int[] getTop() { 119 | return top; 120 | } 121 | 122 | public static int[] getpOrients() { 123 | return pOrients; 124 | } 125 | 126 | public static int[][] getpWidth() { 127 | return pWidth; 128 | } 129 | 130 | public static int[][] getpHeight() { 131 | return pHeight; 132 | } 133 | 134 | public static int[][][] getpBottom() { 135 | return pBottom; 136 | } 137 | 138 | public static int[][][] getpTop() { 139 | return pTop; 140 | } 141 | 142 | 143 | public int getNextPiece() { 144 | return nextPiece; 145 | } 146 | 147 | public boolean hasLost() { 148 | return lost; 149 | } 150 | 151 | public int getRowsCleared() { 152 | return cleared; 153 | } 154 | 155 | public int getTurnNumber() { 156 | return turn; 157 | } 158 | 159 | 160 | 161 | //constructor 162 | public State() { 163 | nextPiece = randomPiece(); 164 | 165 | } 166 | 167 | //random integer, returns 0-6 168 | private int randomPiece() { 169 | return (int)(Math.random()*N_PIECES); 170 | } 171 | 172 | 173 | 174 | 175 | //gives legal moves for 176 | public int[][] legalMoves() { 177 | return legalMoves[nextPiece]; 178 | } 179 | 180 | //make a move based on the move index - its order in the legalMoves list 181 | public void makeMove(int move) { 182 | makeMove(legalMoves[nextPiece][move]); 183 | } 184 | 185 | //make a move based on an array of orient and slot 186 | public void makeMove(int[] move) { 187 | makeMove(move[ORIENT],move[SLOT]); 188 | } 189 | 190 | //returns false if you lose - true otherwise 191 | public boolean makeMove(int orient, int slot) { 192 | turn++; 193 | //height if the first column makes contact 194 | int height = top[slot]-pBottom[nextPiece][orient][0]; 195 | //for each column beyond the first in the piece 196 | for(int c = 1; c < pWidth[nextPiece][orient];c++) { 197 | height = Math.max(height,top[slot+c]-pBottom[nextPiece][orient][c]); 198 | } 199 | 200 | //check if game ended 201 | if(height+pHeight[nextPiece][orient] >= ROWS) { 202 | lost = true; 203 | return false; 204 | } 205 | 206 | 207 | //for each column in the piece - fill in the appropriate blocks 208 | for(int i = 0; i < pWidth[nextPiece][orient]; i++) { 209 | 210 | //from bottom to top of brick 211 | for(int h = height+pBottom[nextPiece][orient][i]; h < height+pTop[nextPiece][orient][i]; h++) { 212 | field[h][i+slot] = turn; 213 | } 214 | } 215 | 216 | //adjust top 217 | for(int c = 0; c < pWidth[nextPiece][orient]; c++) { 218 | top[slot+c]=height+pTop[nextPiece][orient][c]; 219 | } 220 | 221 | int rowsCleared = 0; 222 | 223 | //check for full rows - starting at the top 224 | for(int r = height+pHeight[nextPiece][orient]-1; r >= height; r--) { 225 | //check all columns in the row 226 | boolean full = true; 227 | for(int c = 0; c < COLS; c++) { 228 | if(field[r][c] == 0) { 229 | full = false; 230 | break; 231 | } 232 | } 233 | //if the row was full - remove it and slide above stuff down 234 | if(full) { 235 | rowsCleared++; 236 | cleared++; 237 | //for each column 238 | for(int c = 0; c < COLS; c++) { 239 | 240 | //slide down all bricks 241 | for(int i = r; i < top[c]; i++) { 242 | field[i][c] = field[i+1][c]; 243 | } 244 | //lower the top 245 | top[c]--; 246 | while(top[c]>=1 && field[top[c]-1][c]==0) top[c]--; 247 | } 248 | } 249 | } 250 | 251 | 252 | //pick a new piece 253 | nextPiece = randomPiece(); 254 | 255 | 256 | 257 | return true; 258 | } 259 | 260 | public void draw() { 261 | label.clear(); 262 | label.setPenRadius(); 263 | //outline board 264 | label.line(0, 0, 0, ROWS+5); 265 | label.line(COLS, 0, COLS, ROWS+5); 266 | label.line(0, 0, COLS, 0); 267 | label.line(0, ROWS-1, COLS, ROWS-1); 268 | 269 | //show bricks 270 | 271 | for(int c = 0; c < COLS; c++) { 272 | for(int r = 0; r < top[c]; r++) { 273 | if(field[r][c] != 0) { 274 | drawBrick(c,r); 275 | } 276 | } 277 | } 278 | 279 | for(int i = 0; i < COLS; i++) { 280 | label.setPenColor(Color.red); 281 | label.line(i, top[i], i+1, top[i]); 282 | label.setPenColor(); 283 | } 284 | 285 | label.show(); 286 | 287 | 288 | } 289 | 290 | public static final Color brickCol = Color.gray; 291 | 292 | private void drawBrick(int c, int r) { 293 | label.filledRectangleLL(c, r, 1, 1, brickCol); 294 | label.rectangleLL(c, r, 1, 1); 295 | } 296 | 297 | public void drawNext(int slot, int orient) { 298 | for(int i = 0; i < pWidth[nextPiece][orient]; i++) { 299 | for(int j = pBottom[nextPiece][orient][i]; j = 0; --i, j++) { 524 | db2.setElem(j, db1.getElem(i)); 525 | 526 | } 527 | for (int i = db1.getSize() - 1, j = 0; i >= 0; --i, j++) { 528 | db1.setElem(i, db1.getElem(i)); 529 | 530 | } 531 | return b2; 532 | } 533 | 534 | 535 | 536 | 537 | // write the given text string in the current font, center on (x, y) 538 | public void text(double x, double y, String s) { 539 | offscreen.setFont(font); 540 | FontMetrics metrics = offscreen.getFontMetrics(); 541 | double xs = scaleX(x); 542 | double ys = scaleY(y); 543 | int ws = metrics.stringWidth(s); 544 | int hs = metrics.getDescent(); 545 | offscreen.drawString(s, (float) (xs - ws/2.0), (float) (ys + hs)); 546 | } 547 | 548 | public void textTop(double x, double y, String s) { 549 | offscreen.setFont(font); 550 | FontMetrics metrics = offscreen.getFontMetrics(); 551 | double xs = scaleX(x); 552 | double ys = scaleY(y); 553 | int ws = metrics.stringWidth(s); 554 | int hs = metrics.getDescent(); 555 | offscreen.drawString(s, (float) (xs - ws/2.0), (float) (ys + hs*3)); 556 | } 557 | 558 | public void textLeft(double x, double y, String s,Color c) { 559 | setPenColor(c); 560 | offscreen.setFont(font); 561 | FontMetrics metrics = offscreen.getFontMetrics(); 562 | double xs = scaleX(x); 563 | double ys = scaleY(y); 564 | //int ws = metrics.stringWidth(s); 565 | int hs = metrics.getDescent(); 566 | offscreen.drawString(s, (float) (xs), (float) (ys + hs)); 567 | setPenColor(); 568 | } 569 | 570 | 571 | //Draw text at the appropriate point and color 572 | public void text(double x, double y, String s,Color c) { 573 | setPenColor(c); 574 | offscreen.setFont(font); 575 | FontMetrics metrics = offscreen.getFontMetrics(); 576 | double xs = scaleX(x); 577 | double ys = scaleY(y); 578 | int ws = metrics.stringWidth(s); 579 | int hs = metrics.getDescent(); 580 | offscreen.drawString(s, (float) (xs - ws/2.0), (float) (ys + hs)); 581 | setPenColor(); 582 | } 583 | 584 | // write the given text string in the current font, center on (x, y) sized to w, h 585 | public void text(double x, double y, String s, double w, double h) { 586 | offscreen.setFont(font); 587 | //FontMetrics metrics = offscreen.getFontMetrics(); 588 | double xs = scaleX(x); 589 | double ys = scaleY(y); 590 | double ws = factorX(w); 591 | double hs = factorY(h); 592 | offscreen.drawString(s, (float) (xs - ws/2.0), (float) (ys + hs)); 593 | } 594 | 595 | public void absText(String s, int x, int y) { 596 | offscreen.drawString(s, (float) (x), (float) (y)); 597 | } 598 | 599 | public void textinvert(double x, double y, String s) { 600 | offscreen.setFont(font); 601 | FontMetrics metrics = offscreen.getFontMetrics(); 602 | double xs = scaleX(x); 603 | double ys = scaleY(y); 604 | int ws = metrics.stringWidth(s); 605 | int hs = metrics.getDescent(); 606 | BufferedImage bimage = new BufferedImage(ws, hs,BufferedImage.TYPE_INT_ARGB); 607 | Graphics2D bimagegraphics = bimage.createGraphics(); 608 | bimagegraphics.drawString(s, (float) (xs - ws/2.0), (float) (ys + hs)); 609 | BufferedImage bimage2 = invert(bimage); 610 | offscreen.drawImage(bimage2, (int) Math.round(xs - ws/2.0), 611 | (int) Math.round(ys + hs), null); 612 | } 613 | 614 | 615 | 616 | 617 | // view on-screen, creating new frame if necessary 618 | public void show() { 619 | onscreen.drawImage(offscreenImage, 0, 0, null); 620 | try{ 621 | draw.repaint(); 622 | //frame.paint(frame.getGraphics()); 623 | } 624 | catch(NullPointerException e){ 625 | System.out.println("Null Pointer Exception in showatonce"); 626 | } 627 | 628 | } 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | } 640 | --------------------------------------------------------------------------------