diff --git a/Tetris/Tetris.jar b/Tetris/Tetris.jar new file mode 100644 index 0000000..a8ef4b6 Binary files /dev/null and b/Tetris/Tetris.jar differ diff --git a/Tetris/src/sig/game/Color.java b/Tetris/src/sig/game/Color.java index bf66f2b..f8af02d 100644 --- a/Tetris/src/sig/game/Color.java +++ b/Tetris/src/sig/game/Color.java @@ -1,12 +1,22 @@ package sig.game; public enum Color { - BLUE, - AQUA, - RED, - GREEN, - PURPLE, - YELLOW, - ORANGE, - BLACK + BLUE(java.awt.Color.BLUE), + AQUA(new java.awt.Color(196,196,255)), + RED(java.awt.Color.RED), + GREEN(java.awt.Color.GREEN), + PURPLE(java.awt.Color.MAGENTA), + YELLOW(java.awt.Color.YELLOW), + ORANGE(java.awt.Color.ORANGE), + BLACK(java.awt.Color.BLACK); + + java.awt.Color col; + + Color(java.awt.Color col) { + this.col=col; + } + + java.awt.Color getColor() { + return col; + } } diff --git a/Tetris/src/sig/game/Frame.java b/Tetris/src/sig/game/Frame.java index 30e6cb2..27bf7ce 100644 --- a/Tetris/src/sig/game/Frame.java +++ b/Tetris/src/sig/game/Frame.java @@ -15,6 +15,7 @@ public class Frame { Shape s4, Color col) { shape = new Shape[]{s1,s2,s3,s4}; + s1.col=s2.col=s3.col=s4.col=col; this.col=col; } diff --git a/Tetris/src/sig/game/Game.java b/Tetris/src/sig/game/Game.java index bb5df8f..a5ced8e 100644 --- a/Tetris/src/sig/game/Game.java +++ b/Tetris/src/sig/game/Game.java @@ -1,10 +1,15 @@ package sig.game; import java.awt.Point; +import java.awt.event.KeyEvent; import java.util.Arrays; +import java.util.HashMap; import java.util.Timer; import java.util.TimerTask; +import javax.swing.JFrame; +import javax.swing.WindowConstants; + public class Game { public static Frame[] piecePool; public static Player p; @@ -12,177 +17,200 @@ public class Game { 30,27,25,23,22,20,18,16,14,12, 10,9,8,7,6,5,4,3,2,1 }; + + public static int linesCleared = 0; + public static int level = 0; public static int tickDelay = 60; public static int rotation = 0; //0-3 public static Grid gameGrid = new Grid(); + + public static int keyDelay = 0; + public final static int defaultKeyDelay = 4; public static int rotateDelay = 20; public static int rotateTimer = rotateDelay; public static GameState state = GameState.PLAYING; + public static JFrame frame; + public static GameCanvas canvas; + + public static int lastKey = -1; + + public static HashMap keyMap = new HashMap<>(); + + final public static Frame LPiece = new Frame( + new Shape( + "XOXX" + + "XOXX" + + "XOOX" + + "XXXX"), + new Shape( + "XXXX" + + "XOOO" + + "XOXX" + + "XXXX"), + new Shape( + "XOOX" + + "XXOX" + + "XXOX" + + "XXXX"), + new Shape( + "XXXX" + + "XXOX" + + "OOOX" + + "XXXX") + ,Color.ORANGE); + final public static Frame JPiece = new Frame( + new Shape( + "XXOX" + + "XXOX" + + "XOOX" + + "XXXX"), + new Shape( + "XOXX" + + "XOOO" + + "XXXX" + + "XXXX"), + new Shape( + "XOOX" + + "XOXX" + + "XOXX" + + "XXXX"), + new Shape( + "OOOX" + + "XXOX" + + "XXXX" + + "XXXX") + ,Color.BLUE); + final public static Frame SPiece = new Frame( + new Shape( + "XOXX" + + "XOOX" + + "XXOX" + + "XXXX"), + new Shape( + "XOOX" + + "OOXX" + + "XXXX" + + "XXXX"), + new Shape( + "XOXX" + + "XOOX" + + "XXOX" + + "XXXX"), + new Shape( + "XOOX" + + "OOXX" + + "XXXX" + + "XXXX") + ,Color.RED); + final public static Frame ZPiece = new Frame( + new Shape( + "XXOX" + + "XOOX" + + "XOXX" + + "XXXX"), + new Shape( + "OOXX" + + "XOOX" + + "XXXX" + + "XXXX"), + new Shape( + "XXOX" + + "XOOX" + + "XOXX" + + "XXXX"), + new Shape( + "OOXX" + + "XOOX" + + "XXXX" + + "XXXX") + ,Color.GREEN); + final public static Frame TPiece = new Frame( + new Shape( + "OOOX" + + "XOXX" + + "XXXX" + + "XXXX"), + new Shape( + "XOXX" + + "OOXX" + + "XOXX" + + "XXXX"), + new Shape( + "XOXX" + + "OOOX" + + "XXXX" + + "XXXX"), + new Shape( + "XOXX" + + "XOOX" + + "XOXX" + + "XXXX") + ,Color.PURPLE); + final public static Frame OPiece = new Frame( + new Shape( + "XOOX" + + "XOOX" + + "XXXX" + + "XXXX"), + new Shape( + "XOOX" + + "XOOX" + + "XXXX" + + "XXXX"), + new Shape( + "XOOX" + + "XOOX" + + "XXXX" + + "XXXX"), + new Shape( + "XOOX" + + "XOOX" + + "XXXX" + + "XXXX") + ,Color.YELLOW); + final public static Frame IPiece = new Frame( + new Shape( + "XXOX" + + "XXOX" + + "XXOX" + + "XXOX"), + new Shape( + "OOOO" + + "XXXX" + + "XXXX" + + "XXXX"), + new Shape( + "XXOX" + + "XXOX" + + "XXOX" + + "XXOX"), + new Shape( + "OOOO" + + "XXXX" + + "XXXX" + + "XXXX") + ,Color.AQUA); + public static void main(String[] args) { - final Frame LPiece = new Frame( - new Shape( - "XOXX" - + "XOXX" - + "XOOX" - + "XXXX"), - new Shape( - "XXXX" - + "XOOO" - + "XOXX" - + "XXXX"), - new Shape( - "XOOX" - + "XXOX" - + "XXOX" - + "XXXX"), - new Shape( - "XXXX" - + "XXOX" - + "OOOX" - + "XXXX") - ,Color.ORANGE); - final Frame JPiece = new Frame( - new Shape( - "XXOX" - + "XXOX" - + "XOOX" - + "XXXX"), - new Shape( - "XOXX" - + "XOOO" - + "XXXX" - + "XXXX"), - new Shape( - "XXOX" - + "XXOX" - + "XOOX" - + "XXXX"), - new Shape( - "OOOX" - + "XXOX" - + "XXXX" - + "XXXX") - ,Color.BLUE); - final Frame SPiece = new Frame( - new Shape( - "XOXX" - + "XOOX" - + "XXOX" - + "XXXX"), - new Shape( - "XOOX" - + "OOXX" - + "XXXX" - + "XXXX"), - new Shape( - "XOXX" - + "XOOX" - + "XXOX" - + "XXXX"), - new Shape( - "XOOX" - + "OOXX" - + "XXXX" - + "XXXX") - ,Color.RED); - final Frame ZPiece = new Frame( - new Shape( - "XXOX" - + "XOOX" - + "XOXX" - + "XXXX"), - new Shape( - "OOXX" - + "XOOX" - + "XXXX" - + "XXXX"), - new Shape( - "XXOX" - + "XOOX" - + "XOXX" - + "XXXX"), - new Shape( - "OOXX" - + "XOOX" - + "XXXX" - + "XXXX") - ,Color.GREEN); - final Frame TPiece = new Frame( - new Shape( - "OOOX" - + "XOXX" - + "XXXX" - + "XXXX"), - new Shape( - "XOXX" - + "OOXX" - + "XOXX" - + "XXXX"), - new Shape( - "XOXX" - + "OOOX" - + "XXXX" - + "XXXX"), - new Shape( - "XOXX" - + "XOOX" - + "XOXX" - + "XXXX") - ,Color.PURPLE); - final Frame OPiece = new Frame( - new Shape( - "XOOX" - + "XOOX" - + "XXXX" - + "XXXX"), - new Shape( - "XOOX" - + "XOOX" - + "XXXX" - + "XXXX"), - new Shape( - "XOOX" - + "XOOX" - + "XXXX" - + "XXXX"), - new Shape( - "XOOX" - + "XOOX" - + "XXXX" - + "XXXX") - ,Color.YELLOW); - final Frame IPiece = new Frame( - new Shape( - "XXOX" - + "XXOX" - + "XXOX" - + "XXOX"), - new Shape( - "OOOO" - + "XXXX" - + "XXXX" - + "XXXX"), - new Shape( - "XXOX" - + "XXOX" - + "XXOX" - + "XXOX"), - new Shape( - "OOOO" - + "XXXX" - + "XXXX" - + "XXXX") - ,Color.AQUA); - piecePool = new Frame[] {LPiece,JPiece,SPiece,ZPiece,TPiece,OPiece,IPiece}; p = new Player(); + frame = new JFrame(); + canvas = new GameCanvas(); + frame.addKeyListener(canvas); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.setSize(1024, 576); + frame.setTitle("My Tetris Game"); + frame.add(canvas); + frame.setResizable(false); + frame.setVisible(true); + TimerTask stepTask = new TimerTask() { @Override @@ -194,27 +222,71 @@ public class Game { Timer t = new Timer(); t.schedule(stepTask, 0, (long)Math.floor(1000/60f)); - } static void step() { tickDelay--; - rotateTimer--; - if (tickDelay<=0) { - moveBlock(); - tickDelay=levelDelay[level]; - } - if (rotateTimer<=0) { - //p.rotateCounterClockwise(); - p.moveBlockLeft(); - rotateTimer=rotateDelay; + //rotateTimer--; + if (state!=GameState.LOSE) { + if (tickDelay<=0) { + moveBlock(); + tickDelay=levelDelay[level]; + } + /*if (rotateTimer<=0) { + //p.rotateCounterClockwise(); + p.moveBlockLeft(); + rotateTimer=rotateDelay; + }*/ + HandleKeys(); } DrawBoard(); } + private static void HandleKeys() { + if (AreKeysHeldDown(KeyEvent.VK_LEFT,KeyEvent.VK_A)) { + Game.p.moveBlockLeft(); + UpdateKeyDelay(KeyEvent.VK_LEFT,KeyEvent.VK_A); + } + if (AreKeysHeldDown(KeyEvent.VK_RIGHT,KeyEvent.VK_D)) { + Game.p.moveBlockRight(); + UpdateKeyDelay(KeyEvent.VK_RIGHT,KeyEvent.VK_D); + } + if (AreKeysHeldDown(KeyEvent.VK_S,KeyEvent.VK_DOWN)) { + Game.tickDelay=1; + UpdateKeyDelay(KeyEvent.VK_S,KeyEvent.VK_DOWN); + } + } + + private static void UpdateKeyDelay(int...keys) { + for (int i=0;i=0;y--) { for (int x=0;x<10;x++) { boolean plotted=false; @@ -252,7 +324,7 @@ public class Game { } }*/ - Point[] checkPoints = p.GetPlayerBlocksInGrid(p.getCurrentShape()); + Point[] checkPoints = p.GetPlayerBlocksInGrid(p.getCurrentShape(),p.pos); boolean isOccupied = isOccupied(checkPoints,new Point(0,-1)); @@ -270,6 +342,10 @@ public class Game { //System.out.println("Called: "+y); deletionRows = CheckAndClearFullRows(deletionRows, y); } + linesCleared+=deletionRows; + if (linesCleared/10>level && level<19) { + level=linesCleared/10; + } } private static int CheckAndClearFullRows(int deletionRows, int y) { @@ -298,7 +374,9 @@ public class Game { private static void ShiftBlockDownByNumberOfDeletionRows(int deletionRows, int y, int x) { if (deletionRows>0) { gameGrid.grid[x][y-deletionRows].active=gameGrid.grid[x][y].active; + gameGrid.grid[x][y-deletionRows].color=gameGrid.grid[x][y].color; gameGrid.grid[x][y].active=false; + gameGrid.grid[x][y].color=Color.BLACK; } } @@ -309,10 +387,12 @@ public class Game { private static void SnapPieceThatCollided(Point[] checkPoints) { for (Point point : checkPoints) { //If any point in the grid is outside, this is our lose condition. - if (point.y>=gameGrid.grid[0].length) { + if (point.y>=20) { state = GameState.LOSE; + } else { + gameGrid.grid[point.x][point.y].active=true; + gameGrid.grid[point.x][point.y].color=p.getCurrentShape().col; } - gameGrid.grid[point.x][point.y].active=true; } p.ShuffleNextPiece(); } @@ -325,6 +405,7 @@ public class Game { for (int i=0;i=gameGrid.grid[0].length|| point.x+offset.x>=gameGrid.grid.length|| @@ -333,6 +414,7 @@ public class Game { isOccupied=true; break; } + //System.out.println("Pass"); } return isOccupied; } diff --git a/Tetris/src/sig/game/GameCanvas.java b/Tetris/src/sig/game/GameCanvas.java new file mode 100644 index 0000000..821a6e1 --- /dev/null +++ b/Tetris/src/sig/game/GameCanvas.java @@ -0,0 +1,132 @@ +package sig.game; + +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.Arrays; +import java.util.List; + +import javax.swing.JPanel; + +import sig.utils.DrawUtils; + +public class GameCanvas extends JPanel implements KeyListener{ + public final static Font displayText = new Font("Consolas",Font.BOLD,24); + + public final static int WINDOW_WIDTH = 1024; + public final static int WINDOW_HEIGHT = 576; + + public final static int BLOCK_SIZE = 24; + public final static int FIELD_WIDTH = BLOCK_SIZE*10; + public final static int FIELD_HEIGHT = BLOCK_SIZE*20; + + public final static int FIELD_STARTX = WINDOW_WIDTH/2-FIELD_WIDTH/2; + public final static int FIELD_STARTY = WINDOW_HEIGHT/2+FIELD_HEIGHT/2; + + public final static int LINES_CLEARED_DISPLAYX = WINDOW_WIDTH/2+180; + public final static int LINES_CLEARED_DISPLAYY = WINDOW_HEIGHT/2; + + public final static int LEVEL_DISPLAYX = WINDOW_WIDTH/2+180; + public final static int LEVEL_DISPLAYY = LINES_CLEARED_DISPLAYY-64; + + public final static int NEXTPIECE_DISPLAYX = WINDOW_WIDTH/2+180; + public final static int NEXTPIECE_DISPLAYY = LEVEL_DISPLAYY-128; + + public void paintComponent(Graphics g) { + g.setColor(java.awt.Color.DARK_GRAY); + g.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + //System.out.println("Redrawn"); + Point[] checkPoints = Game.p.GetPlayerBlocksInGrid(Game.p.getCurrentShape(),Game.p.pos); + for (int y=0;y<20;y++) { + for (int x=0;x<10;x++) { + boolean plotted=false; + for (Point point : checkPoints) { + if (point.x==x && point.y==y) { + plotted=true; + //System.out.print('P'); + g.setColor((Game.state==GameState.LOSE) + ?java.awt.Color.LIGHT_GRAY: + Game.p.getCurrentShape().col.getColor()); + Point blockCoord=getBlockCoordinateToPixelCoordinate(point); + g.fill3DRect(blockCoord.x, blockCoord.y, BLOCK_SIZE, BLOCK_SIZE, false); + break; + } + } + if (!plotted) { + g.setColor((Game.state==GameState.LOSE&&Game.gameGrid.grid[x][y].active) + ?java.awt.Color.LIGHT_GRAY:Game.gameGrid.grid[x][y].color.getColor()); + Point blockCoord=getBlockCoordinateToPixelCoordinate(new Point(x,y)); + g.fill3DRect(blockCoord.x, blockCoord.y, BLOCK_SIZE, BLOCK_SIZE, false); + } + } + } + Point[] nextPoints = Game.p.GetPlayerBlocksInGrid(Game.p.getNextShape(),new Point(0,0)); + + g.setColor(java.awt.Color.BLACK); + g.fill3DRect(NEXTPIECE_DISPLAYX, NEXTPIECE_DISPLAYY, 104, 104, true); + for (Point p : nextPoints) { + g.setColor(Game.p.getNextShape().col.getColor()); + g.fill3DRect(p.x*BLOCK_SIZE+NEXTPIECE_DISPLAYX+52, p.y*BLOCK_SIZE+NEXTPIECE_DISPLAYY+52, BLOCK_SIZE, BLOCK_SIZE, false); + } + g.setColor(java.awt.Color.WHITE); + g.setFont(displayText); + g.drawString("NEXT",NEXTPIECE_DISPLAYX,NEXTPIECE_DISPLAYY); + g.setColor(java.awt.Color.WHITE); + g.setFont(displayText); + g.drawString("LINES",LINES_CLEARED_DISPLAYX,LINES_CLEARED_DISPLAYY); + g.drawString(Integer.toString(Game.linesCleared),LINES_CLEARED_DISPLAYX+6,LINES_CLEARED_DISPLAYY+24); + g.drawString("LEVEL",LEVEL_DISPLAYX,LEVEL_DISPLAYY); + g.drawString(Integer.toString(Game.level+1),LEVEL_DISPLAYX+6,LEVEL_DISPLAYY+24); + if (Game.state==GameState.LOSE) { + DrawUtils.drawCenteredText(g, g.getFont(),this, WINDOW_WIDTH/2, WINDOW_HEIGHT/2, java.awt.Color.WHITE, "Press R to Restart!"); + } + } + + public Point getBlockCoordinateToPixelCoordinate(Point blockLoc) { + return new Point(FIELD_STARTX+BLOCK_SIZE*blockLoc.x,FIELD_STARTY-BLOCK_SIZE*blockLoc.y); + } + + @Override + public void keyPressed(KeyEvent e) { + List keys = Arrays.asList( + KeyEvent.VK_LEFT,KeyEvent.VK_A, + KeyEvent.VK_RIGHT,KeyEvent.VK_D, + KeyEvent.VK_S,KeyEvent.VK_DOWN + ); + Game.lastKey=e.getKeyCode(); + /*if ((Game.keyMap.containsKey(e.getKeyCode())&&!Game.keyMap.get(e.getKeyCode())&&keys.contains(e.getKeyCode()) + )||!Game.keyMap.containsKey(e.getKeyCode())) { + System.out.println("Executing"); + Game.keyDelay=20; + }*/ + if (Game.keyMap.containsKey(e.getKeyCode())) { + Game.keyMap.put(e.getKeyCode(),Game.keyMap.get(e.getKeyCode()).updatePress(true)); + } else { + Game.keyMap.put(e.getKeyCode(),new Key(true)); + } + if (e.getKeyCode()==KeyEvent.VK_Z||e.getKeyCode()==KeyEvent.VK_COMMA) { + Game.p.rotateClockwise(); + } + if (e.getKeyCode()==KeyEvent.VK_X||e.getKeyCode()==KeyEvent.VK_PERIOD) { + Game.p.rotateCounterClockwise(); + } + if (e.getKeyCode()==KeyEvent.VK_R && Game.state==GameState.LOSE) { + Game.p = new Player(); + Game.gameGrid = new Grid(); + Game.level=0; + Game.linesCleared=0; + Game.state=GameState.PLAYING; + } + } + + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + Game.keyMap.put(e.getKeyCode(),Game.keyMap.get(e.getKeyCode()).updatePress(false)); + } +} diff --git a/Tetris/src/sig/game/Grid.java b/Tetris/src/sig/game/Grid.java index 12b9716..6c4b4d2 100644 --- a/Tetris/src/sig/game/Grid.java +++ b/Tetris/src/sig/game/Grid.java @@ -5,7 +5,7 @@ import java.util.Arrays; public class Grid { final int GRID_WIDTH = 10; - final int GRID_HEIGHT = 20; + final int GRID_HEIGHT = 24; Block[][] grid = new Block[GRID_WIDTH][GRID_HEIGHT]; diff --git a/Tetris/src/sig/game/Key.java b/Tetris/src/sig/game/Key.java new file mode 100644 index 0000000..2ec96f7 --- /dev/null +++ b/Tetris/src/sig/game/Key.java @@ -0,0 +1,18 @@ +package sig.game; + +public class Key { + boolean pressed; + int timer; + public Key(boolean pressed) { + this.pressed=pressed; + this.timer=0; + } + public Key updatePress(boolean pressed) { + this.pressed=pressed; + return this; + } + public Key setTimer(int timer) { + this.timer=timer; + return this; + } +} diff --git a/Tetris/src/sig/game/Player.java b/Tetris/src/sig/game/Player.java index 24d3e9c..51c9d8f 100644 --- a/Tetris/src/sig/game/Player.java +++ b/Tetris/src/sig/game/Player.java @@ -20,10 +20,10 @@ public class Player { public void ShuffleNextPiece() { piece = nextPiece.clone(); nextPiece = SelectRandomTetrimino(); - pos = new Point(5,20); + pos = new Point(5,21); } - public Point[] GetPlayerBlocksInGrid(Shape s) { + public Point[] GetPlayerBlocksInGrid(Shape s,Point offset) { Point[] checkPoints = new Point[4]; Shape tetShape = s; int pointsInserted=0; @@ -31,7 +31,7 @@ public class Player { for (int y=0;y=0) { + //System.out.println(p.x+","+p.y); isFree=false; break; } @@ -92,14 +93,19 @@ public class Player { private void RotateIfSpaceIsFree(Point[] checkPoints,RotationDirection dir) { int[] offset = new int[]{0,-1,1}; - int freeOffset = -1; + if (Game.p.getCurrentShape().equals(Game.IPiece.shape[Game.rotation])) { + //System.out.println("Is I Piece."); + offset = new int[] {0,-1,1,-2,2,-3}; + } + int freeOffset = -99999; for (int i=0;i0) { - AttributedString as = new AttributedString(message); - as.addAttribute(TextAttribute.FONT, MyPanel.programFont); + /*AttributedString as = new AttributedString(message); + as.addAttribute(TextAttribute.FONT, MyPanel.programFont);*/ g.setColor(color); - g.drawString(as.getIterator(),(int)x,(int)y); + g.drawString(message,(int)x,(int)y); } } public static void drawTextFont(Graphics g, Font font, double x, double y, Color color, String message) { @@ -90,11 +91,11 @@ public class DrawUtils { /** * Centers the text along the X Axis. */ - public static void drawCenteredText(Graphics g, Font font, int x, int y, Color color, String text) { + public static void drawCenteredText(Graphics g, Font font,JPanel panel, int x, int y, Color color, String text) { AttributedString as = new AttributedString(text); as.addAttribute(TextAttribute.FONT, font); g.setColor(color); - Rectangle2D textBounds = TextUtils.calculateStringBoundsFont(text, font); + Rectangle2D textBounds = TextUtils.calculateStringBoundsFont(panel,text, font); g.drawString(as.getIterator(),(int)(x-textBounds.getWidth()/2),(int)(y+textBounds.getHeight())); } diff --git a/Tetris/src/sig/utils/TextUtils.java b/Tetris/src/sig/utils/TextUtils.java index 693f45f..4e0002d 100644 --- a/Tetris/src/sig/utils/TextUtils.java +++ b/Tetris/src/sig/utils/TextUtils.java @@ -6,12 +6,13 @@ import java.awt.font.FontRenderContext; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.List; -import sig.MyRobot; + +import javax.swing.JPanel; public class TextUtils { - public static Rectangle2D calculateStringBoundsFont(String msg, Font font) { - FontRenderContext frc = MyRobot.p.getFontMetrics(font).getFontRenderContext(); + public static Rectangle2D calculateStringBoundsFont(JPanel panel,String msg, Font font) { + FontRenderContext frc = panel.getFontMetrics(font).getFontRenderContext(); return font.getStringBounds(msg, frc); }