diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..576dddd Binary files /dev/null and b/.DS_Store differ diff --git a/.classpath b/.classpath index fb565a5..e055c44 100644 --- a/.classpath +++ b/.classpath @@ -1,6 +1,36 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09e3bc9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/target/ diff --git a/.project b/.project index ddf9a01..d83a904 100644 --- a/.project +++ b/.project @@ -10,8 +10,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..714351a --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..34b28cc --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Tetris +This is a Tetris game that can be played on your computer by running the eclipse project. It is easy to play using simple keyboard controls that allow you to move pieces side to side, rotate pieces, drop pieces quickly to the bottom, and switch the current piece with the next one. diff --git a/bin/org/psnbtech/BoardPanel.class b/bin/org/psnbtech/BoardPanel.class deleted file mode 100644 index 142911a..0000000 Binary files a/bin/org/psnbtech/BoardPanel.class and /dev/null differ diff --git a/bin/org/psnbtech/Clock.class b/bin/org/psnbtech/Clock.class deleted file mode 100644 index 61f706c..0000000 Binary files a/bin/org/psnbtech/Clock.class and /dev/null differ diff --git a/bin/org/psnbtech/SidePanel.class b/bin/org/psnbtech/SidePanel.class deleted file mode 100644 index 01ce5fb..0000000 Binary files a/bin/org/psnbtech/SidePanel.class and /dev/null differ diff --git a/bin/org/psnbtech/Tetris$1.class b/bin/org/psnbtech/Tetris$1.class deleted file mode 100644 index a5b3ea1..0000000 Binary files a/bin/org/psnbtech/Tetris$1.class and /dev/null differ diff --git a/bin/org/psnbtech/Tetris.class b/bin/org/psnbtech/Tetris.class deleted file mode 100644 index 26c5acb..0000000 Binary files a/bin/org/psnbtech/Tetris.class and /dev/null differ diff --git a/bin/org/psnbtech/TileType.class b/bin/org/psnbtech/TileType.class deleted file mode 100644 index ac590ae..0000000 Binary files a/bin/org/psnbtech/TileType.class and /dev/null differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a3362ef --- /dev/null +++ b/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + Tetris + Tetris + 0.0.1-SNAPSHOT + + + org.mockito + mockito-core + 2.18.3 + test + + + + src + test + + + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + + \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..6a8d515 Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/org/.DS_Store b/src/org/.DS_Store new file mode 100644 index 0000000..ed21fa7 Binary files /dev/null and b/src/org/.DS_Store differ diff --git a/src/org/psnbtech/.DS_Store b/src/org/psnbtech/.DS_Store new file mode 100644 index 0000000..266c0d4 Binary files /dev/null and b/src/org/psnbtech/.DS_Store differ diff --git a/src/org/psnbtech/BoardPanel.java b/src/org/psnbtech/BoardPanel.java index 2951ce5..e0f7ba0 100644 --- a/src/org/psnbtech/BoardPanel.java +++ b/src/org/psnbtech/BoardPanel.java @@ -345,7 +345,7 @@ public void paintComponent(Graphics g) { * down until we hit a row that would cause a collision. */ Color base = type.getBaseColor(); - base = new Color(base.getRed(), base.getGreen(), base.getBlue(), 20); + base = new Color(base.getRed(), base.getGreen(), base.getBlue(), 50); for(int lowest = pieceRow; lowest < ROW_COUNT; lowest++) { //If no collision is detected, try the next row. if(isValidAndEmpty(type, pieceCol, lowest, rotation)) { diff --git a/src/org/psnbtech/SidePanel.java b/src/org/psnbtech/SidePanel.java index 4c8da70..8415b4b 100644 --- a/src/org/psnbtech/SidePanel.java +++ b/src/org/psnbtech/SidePanel.java @@ -64,12 +64,12 @@ public class SidePanel extends JPanel { /** * The y coordinate of the stats category. */ - private static final int STATS_INSET = 175; + private static final int STATS_INSET = 155; /** * The y coordinate of the controls category. */ - private static final int CONTROLS_INSET = 300; + private static final int CONTROLS_INSET = 260; /** * The number of pixels to offset between each string. @@ -103,7 +103,7 @@ public class SidePanel extends JPanel { public SidePanel(Tetris tetris) { this.tetris = tetris; - setPreferredSize(new Dimension(200, BoardPanel.PANEL_HEIGHT)); + setPreferredSize(new Dimension(250, BoardPanel.PANEL_HEIGHT)); setBackground(Color.BLACK); } @@ -136,11 +136,13 @@ public void paintComponent(Graphics g) { g.setFont(LARGE_FONT); g.drawString("Controls", SMALL_INSET, offset = CONTROLS_INSET); g.setFont(SMALL_FONT); - g.drawString("A - Move Left", LARGE_INSET, offset += TEXT_STRIDE); - g.drawString("D - Move Right", LARGE_INSET, offset += TEXT_STRIDE); - g.drawString("Q - Rotate Anticlockwise", LARGE_INSET, offset += TEXT_STRIDE); - g.drawString("E - Rotate Clockwise", LARGE_INSET, offset += TEXT_STRIDE); - g.drawString("S - Drop", LARGE_INSET, offset += TEXT_STRIDE); + g.drawString("< or Q - Rotate Anticlockwise", LARGE_INSET, offset += TEXT_STRIDE); + g.drawString("> or E- Rotate Clockwise", LARGE_INSET, offset += TEXT_STRIDE); + g.drawString("Left Arrow or D- Move Left", LARGE_INSET, offset += TEXT_STRIDE); + g.drawString("Right Arrow or A- Move Right", LARGE_INSET, offset += TEXT_STRIDE); + g.drawString("Up Arrow or W- Hard Drop", LARGE_INSET, offset += TEXT_STRIDE); + g.drawString("Down Arrow or S- Soft Drop", LARGE_INSET, offset += TEXT_STRIDE); + g.drawString("Shift or R- Switch Next", LARGE_INSET, offset += TEXT_STRIDE); g.drawString("P - Pause Game", LARGE_INSET, offset += TEXT_STRIDE); /* diff --git a/src/org/psnbtech/Tetris.java b/src/org/psnbtech/Tetris.java index b9813df..fe09908 100644 --- a/src/org/psnbtech/Tetris.java +++ b/src/org/psnbtech/Tetris.java @@ -141,116 +141,8 @@ private Tetris() { /* * Adds a custom anonymous KeyListener to the frame. */ - addKeyListener(new KeyAdapter() { - - @Override - public void keyPressed(KeyEvent e) { - - switch(e.getKeyCode()) { - - /* - * Drop - When pressed, we check to see that the game is not - * paused and that there is no drop cooldown, then set the - * logic timer to run at a speed of 25 cycles per second. - */ - case KeyEvent.VK_S: - if(!isPaused && dropCooldown == 0) { - logicTimer.setCyclesPerSecond(25.0f); - } - break; - - /* - * Move Left - When pressed, we check to see that the game is - * not paused and that the position to the left of the current - * position is valid. If so, we decrement the current column by 1. - */ - case KeyEvent.VK_A: - if(!isPaused && board.isValidAndEmpty(currentType, currentCol - 1, currentRow, currentRotation)) { - currentCol--; - } - break; - - /* - * Move Right - When pressed, we check to see that the game is - * not paused and that the position to the right of the current - * position is valid. If so, we increment the current column by 1. - */ - case KeyEvent.VK_D: - if(!isPaused && board.isValidAndEmpty(currentType, currentCol + 1, currentRow, currentRotation)) { - currentCol++; - } - break; - - /* - * Rotate Anticlockwise - When pressed, check to see that the game is not paused - * and then attempt to rotate the piece anticlockwise. Because of the size and - * complexity of the rotation code, as well as it's similarity to clockwise - * rotation, the code for rotating the piece is handled in another method. - */ - case KeyEvent.VK_Q: - if(!isPaused) { - rotatePiece((currentRotation == 0) ? 3 : currentRotation - 1); - } - break; - - /* - * Rotate Clockwise - When pressed, check to see that the game is not paused - * and then attempt to rotate the piece clockwise. Because of the size and - * complexity of the rotation code, as well as it's similarity to anticlockwise - * rotation, the code for rotating the piece is handled in another method. - */ - case KeyEvent.VK_E: - if(!isPaused) { - rotatePiece((currentRotation == 3) ? 0 : currentRotation + 1); - } - break; - - /* - * Pause Game - When pressed, check to see that we're currently playing a game. - * If so, toggle the pause variable and update the logic timer to reflect this - * change, otherwise the game will execute a huge number of updates and essentially - * cause an instant game over when we unpause if we stay paused for more than a - * minute or so. - */ - case KeyEvent.VK_P: - if(!isGameOver && !isNewGame) { - isPaused = !isPaused; - logicTimer.setPaused(isPaused); - } - break; - - /* - * Start Game - When pressed, check to see that we're in either a game over or new - * game state. If so, reset the game. - */ - case KeyEvent.VK_ENTER: - if(isGameOver || isNewGame) { - resetGame(); - } - break; - - } - } - - @Override - public void keyReleased(KeyEvent e) { - - switch(e.getKeyCode()) { - - /* - * Drop - When released, we set the speed of the logic timer - * back to whatever the current game speed is and clear out - * any cycles that might still be elapsed. - */ - case KeyEvent.VK_S: - logicTimer.setCyclesPerSecond(gameSpeed); - logicTimer.reset(); - break; - } - - } - - }); + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(this); + addKeyListener(tetrisKeyAdapter); /* * Here we resize the frame to hold the BoardPanel and SidePanel instances, @@ -323,7 +215,7 @@ private void updateGame() { /* * Check to see if the piece's position can move down to the next row. */ - if(board.isValidAndEmpty(currentType, currentCol, currentRow + 1, currentRotation)) { + if(board.isValidAndEmpty(currentType, currentCol, currentRow + 1, getCurrentRotation())) { //Increment the current row if it's safe to do so. currentRow++; } else { @@ -331,7 +223,7 @@ private void updateGame() { * We've either reached the bottom of the board, or landed on another piece, so * we need to add the piece to the board. */ - board.addPiece(currentType, currentCol, currentRow, currentRotation); + board.addPiece(currentType, currentCol, currentRow, getCurrentRotation()); /* * Check to see if adding the new piece resulted in any cleared lines. If so, @@ -383,7 +275,7 @@ private void renderGame() { * Resets the game variables to their default values at the start * of a new game. */ - private void resetGame() { + void resetGame() { this.level = 1; this.score = 0; this.gameSpeed = 1.0f; @@ -408,14 +300,14 @@ private void spawnPiece() { this.currentType = nextType; this.currentCol = currentType.getSpawnColumn(); this.currentRow = currentType.getSpawnRow(); - this.currentRotation = 0; + this.setCurrentRotation(0); this.nextType = TileType.values()[random.nextInt(TYPE_COUNT)]; /* * If the spawn point is invalid, we need to pause the game and flag that we've lost * because it means that the pieces on the board have gotten too high. */ - if(!board.isValidAndEmpty(currentType, currentCol, currentRow, currentRotation)) { + if(!board.isValidAndEmpty(currentType, currentCol, currentRow, getCurrentRotation())) { this.isGameOver = true; logicTimer.setPaused(true); } @@ -423,9 +315,9 @@ private void spawnPiece() { /** * Attempts to set the rotation of the current piece to newRotation. - * @param newRotation The rotation of the new peice. + * @param newRotation The rotation of the new piece. */ - private void rotatePiece(int newRotation) { + void rotatePiece(int newRotation) { /* * Sometimes pieces will need to be moved when rotated to avoid clipping * out of the board (the I piece is a good example of this). Here we store @@ -468,7 +360,7 @@ private void rotatePiece(int newRotation) { * position of the piece. */ if(board.isValidAndEmpty(currentType, newColumn, newRow, newRotation)) { - currentRotation = newRotation; + setCurrentRotation(newRotation); currentRow = newRow; currentCol = newColumn; } @@ -551,9 +443,125 @@ public int getPieceRow() { * @return The rotation. */ public int getPieceRotation() { + return getCurrentRotation(); + } + + /** + * Increments current row. + * @return The row. + */ + public int incrementCurrentRow() { + return currentRow++; + } + + /** + * Decrements current row. + * @return The row. + */ + public int decrementCurrentRow() { + return currentRow--; + } + + /** + * Increments current column. + * @return The column. + */ + public int incrementCurrentCol() { + return currentCol++; + } + + /** + * Decrements current column. + * @return The column. + */ + public int decrementCurrentCol() { + return currentCol--; + } + + public BoardPanel getBoard() { + return board; + } + + public void setBoard(BoardPanel board) { + this.board = board; + } + + public Clock getLogicTimer() { + return logicTimer; + } + + public void setLogicTimer(Clock logicTimer) { + this.logicTimer = logicTimer; + } + + public TileType getCurrentType() { + return currentType; + } + + public void setCurrentType(TileType currentType) { + this.currentType = currentType; + } + + public TileType getNextType() { + return nextType; + } + + public void setNextType(TileType nextType) { + this.nextType = nextType; + } + + public int getCurrentCol() { + return currentCol; + } + + public void setCurrentCol(int currentCol) { + this.currentCol = currentCol; + } + + public int getCurrentRow() { + return currentRow; + } + + public void setCurrentRow(int currentRow) { + this.currentRow = currentRow; + } + + public int getDropCooldown() { + return dropCooldown; + } + + public void setDropCooldown(int dropCooldown) { + this.dropCooldown = dropCooldown; + } + + public void setPaused(boolean isPaused) { + this.isPaused = isPaused; + } + + public void setNewGame(boolean isNewGame) { + this.isNewGame = isNewGame; + } + + public void setGameOver(boolean isGameOver) { + this.isGameOver = isGameOver; + } + + public int getCurrentRotation() { return currentRotation; } + public void setCurrentRotation(int currentRotation) { + this.currentRotation = currentRotation; + } + + public float getGameSpeed() { + return gameSpeed; + } + + public void setGameSpeed(float gameSpeed) { + this.gameSpeed = gameSpeed; + } + /** * Entry-point of the game. Responsible for creating and starting a new * game instance. @@ -563,5 +571,5 @@ public static void main(String[] args) { Tetris tetris = new Tetris(); tetris.startGame(); } - + } diff --git a/src/org/psnbtech/TetrisKeyAdapter.java b/src/org/psnbtech/TetrisKeyAdapter.java new file mode 100644 index 0000000..3b99954 --- /dev/null +++ b/src/org/psnbtech/TetrisKeyAdapter.java @@ -0,0 +1,163 @@ +package org.psnbtech; + +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +public class TetrisKeyAdapter implements KeyListener { + + private Tetris tetris; + + public TetrisKeyAdapter(Tetris tetris) { + this.tetris = tetris; + } + + @Override + public void keyPressed(KeyEvent e) { + + switch (e.getKeyCode()) { + + /* + * Soft Drop - When pressed, we check to see that the game is not paused and + * that there is no drop cooldown, then set the logic timer to run at a speed of + * 25 cycles per second. + */ + case KeyEvent.VK_S: + case KeyEvent.VK_DOWN: + if (!tetris.isPaused() && tetris.getDropCooldown() == 0) { + tetris.getLogicTimer().setCyclesPerSecond(25.0f); + } + break; + + /* + * Hard Drop - When pressed, we check to see that the game is not paused and + * then check for the lowest available row and drop the piece to there. + */ + case KeyEvent.VK_W: + case KeyEvent.VK_UP: + if (!tetris.isPaused()) { + tetris.getBoard(); + for (int lowest = tetris.getCurrentRow(); lowest < BoardPanel.ROW_COUNT; lowest++) { + // If no collision is detected, try the next row. + if (tetris.getBoard().isValidAndEmpty(tetris.getCurrentType(), tetris.getCurrentCol(), lowest, tetris.getCurrentRotation())) { + tetris.setCurrentRow(lowest); + } + + } + } + break; + + /* + * Move Left - When pressed, we check to see that the game is not paused and + * that the position to the left of the current position is valid. If so, we + * decrement the current column by 1. + */ + case KeyEvent.VK_A: + case KeyEvent.VK_LEFT: + if (!tetris.isPaused() && tetris.getBoard().isValidAndEmpty(tetris.getCurrentType(), tetris.getCurrentCol() - 1, tetris.getCurrentRow(), tetris.getCurrentRotation())) { + tetris.decrementCurrentCol(); + } + break; + + /* + * Move Right - When pressed, we check to see that the game is not paused and + * that the position to the right of the current position is valid. If so, we + * increment the current column by 1. + */ + case KeyEvent.VK_D: + case KeyEvent.VK_RIGHT: + if (!tetris.isPaused() && tetris.getBoard().isValidAndEmpty(tetris.getCurrentType(), tetris.getCurrentCol() + 1, tetris.getCurrentRow(), tetris.getCurrentRotation())) { + tetris.incrementCurrentCol(); + } + break; + + /* + * Switch Piece - When pressed, we check to see that the game is not paused and + * then switch the current piece with the next piece. + */ + case KeyEvent.VK_R: + case KeyEvent.VK_SHIFT: + if (!tetris.isPaused()) { + TileType bufferType; + bufferType = tetris.getCurrentType(); + tetris.setCurrentType(tetris.getNextType()); + tetris.setNextType(bufferType); + } + break; + + /* + * Rotate Anticlockwise - When pressed, check to see that the game is not paused + * and then attempt to rotate the piece anticlockwise. Because of the size and + * complexity of the rotation code, as well as it's similarity to clockwise + * rotation, the code for rotating the piece is handled in another method. + */ + case KeyEvent.VK_Q: + case KeyEvent.VK_COMMA: + if (!tetris.isPaused()) { + tetris.rotatePiece((tetris.getCurrentRotation() == 0) ? 3 : tetris.getCurrentRotation() - 1); + } + break; + + /* + * Rotate Clockwise - When pressed, check to see that the game is not paused and + * then attempt to rotate the piece clockwise. Because of the size and + * complexity of the rotation code, as well as it's similarity to anticlockwise + * rotation, the code for rotating the piece is handled in another method. + */ + case KeyEvent.VK_E: + case KeyEvent.VK_PERIOD: + if (!tetris.isPaused()) { + tetris.rotatePiece((tetris.getCurrentRotation() == 3) ? 0 : tetris.getCurrentRotation() + 1); + } + break; + + /* + * Pause Game - When pressed, check to see that we're currently playing a game. + * If so, toggle the pause variable and update the logic timer to reflect this + * change, otherwise the game will execute a huge number of updates and + * essentially cause an instant game over when we unpause if we stay paused for + * more than a minute or so. + */ + case KeyEvent.VK_P: + if (!tetris.isGameOver() && !tetris.isNewGame()) { + tetris.setPaused(!tetris.isPaused()); + tetris.getLogicTimer().setPaused(tetris.isPaused()); + } + break; + + /* + * Start Game - When pressed, check to see that we're in either a game over or + * new game state. If so, reset the game. + */ + case KeyEvent.VK_ENTER: + if (tetris.isGameOver() || tetris.isNewGame()) { + tetris.resetGame(); + } + break; + + } + } + + @Override + public void keyReleased(KeyEvent e) { + + switch (e.getKeyCode()) { + + /* + * Drop - When released, we set the speed of the logic timer back to whatever + * the current game speed is and clear out any cycles that might still be + * elapsed. + */ + case KeyEvent.VK_S: + case KeyEvent.VK_DOWN: + tetris.getLogicTimer().setCyclesPerSecond(tetris.getGameSpeed()); + tetris.getLogicTimer().reset(); + break; + } + + } + + @Override + public void keyTyped(KeyEvent e) { + + } +} diff --git a/test/org/psnbtech/TetrisKeyAdapterTest.java b/test/org/psnbtech/TetrisKeyAdapterTest.java new file mode 100644 index 0000000..3f3c500 --- /dev/null +++ b/test/org/psnbtech/TetrisKeyAdapterTest.java @@ -0,0 +1,403 @@ +package org.psnbtech; + + +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.awt.event.KeyEvent; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; + +public class TetrisKeyAdapterTest { + + @Mock + Tetris tetris; + @Mock + Clock clock; + @Mock + BoardPanel boardPanel; + @Mock + KeyEvent keyEvent; + + TetrisKeyAdapter tetrisKeyAdapter; + + @BeforeEach + public void before() { + initMocks(this); + tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + when(tetris.getLogicTimer()).thenReturn(clock); + doReturn(boardPanel).when(tetris).getBoard(); + } + + @Test + public void testDownKeyPressed() { + //given + doReturn(false).when(tetris).isPaused(); + doReturn(0).when(tetris).getDropCooldown(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_DOWN); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris).getDropCooldown(); + verify(clock).setCyclesPerSecond(25.0f); + } + + @Test + public void testDownKeyPressedPaused() { + //given + doReturn(true).when(tetris).isPaused(); + doReturn(0).when(tetris).getDropCooldown(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_DOWN); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris, never()).getDropCooldown(); + verify(clock, never()).setCyclesPerSecond(25.0f); + } + + @Test + public void testDownKeyPressedCooldown() { + //given + doReturn(false).when(tetris).isPaused(); + doReturn(1).when(tetris).getDropCooldown(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_DOWN); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris).getDropCooldown(); + verify(clock, never()).setCyclesPerSecond(25.0f); + } + + @Test + public void testUpKeyPressed() { + //given + doReturn(false).when(tetris).isPaused(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_UP); + when(boardPanel.isValidAndEmpty(Mockito.any(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt())).thenReturn(true); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris, atLeast(1)).getBoard(); + verify(tetris).getCurrentRow(); + verify(tetris).setCurrentRow(0); + } + + @Test + public void testUpKeyPressedPaused() { + //given + doReturn(true).when(tetris).isPaused(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_UP); + when(boardPanel.isValidAndEmpty(Mockito.any(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt())).thenReturn(true); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris, never()).getBoard(); + verify(tetris, never()).getCurrentRow(); + verify(tetris, never()).setCurrentRow(0); + } + + @Test + public void testLeftKeyPressed() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(false).when(tetris).isPaused(); + when(boardPanel.isValidAndEmpty(Mockito.any(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt())).thenReturn(true); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_LEFT); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris).getBoard(); + verify(tetris).decrementCurrentCol(); + } + + @Test + public void testLeftKeyPressedPaused() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(true).when(tetris).isPaused(); + when(boardPanel.isValidAndEmpty(Mockito.any(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt())).thenReturn(true); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_LEFT); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris, never()).getBoard(); + verify(tetris, never()).decrementCurrentCol(); + } + + @Test + public void testRightKeyPressed() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(false).when(tetris).isPaused(); + when(boardPanel.isValidAndEmpty(Mockito.any(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt())).thenReturn(true); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_RIGHT); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris).getBoard(); + verify(tetris).incrementCurrentCol(); + } + + @Test + public void testRightKeyPressedPaused() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(true).when(tetris).isPaused(); + when(boardPanel.isValidAndEmpty(Mockito.any(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt())).thenReturn(true); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_RIGHT); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris, never()).getBoard(); + verify(tetris, never()).incrementCurrentCol(); + } + + @Test + public void testShiftKeyPressed() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(false).when(tetris).isPaused(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_SHIFT); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + TileType bufferType = verify(tetris).getCurrentType(); + verify(tetris).getCurrentType(); + verify(tetris).setCurrentType(tetris.getNextType()); + verify(tetris).setNextType(bufferType); + } + + @Test + public void testShiftKeyPressedPaused() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(true).when(tetris).isPaused(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_SHIFT); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + TileType bufferType = verify(tetris, never()).getCurrentType(); + verify(tetris, never()).getCurrentType(); + verify(tetris, never()).setCurrentType(tetris.getNextType()); + verify(tetris, never()).setNextType(bufferType); + } + + @Test + public void testCommaKeyPressed() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(false).when(tetris).isPaused(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_COMMA); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris, atLeast(1)).rotatePiece((tetris.getCurrentRotation() == 0) ? 3 : tetris.getCurrentRotation() - 1); + } + + @Test + public void testCommaKeyPressedPaused() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(true).when(tetris).isPaused(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_COMMA); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris, never()).rotatePiece((tetris.getCurrentRotation() == 0) ? 3 : tetris.getCurrentRotation() - 1); + } + + @Test + public void testPeriodKeyPressed() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(false).when(tetris).isPaused(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_PERIOD); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris, atLeast(1)).rotatePiece((tetris.getCurrentRotation() == 3) ? 0 : tetris.getCurrentRotation() + 1); + } + + @Test + public void testPeriodKeyPressedPaused() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(true).when(tetris).isPaused(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_PERIOD); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).isPaused(); + verify(tetris, never()).rotatePiece((tetris.getCurrentRotation() == 3) ? 0 : tetris.getCurrentRotation() + 1); + } + + @Test + public void testPauseKeyPressed() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(false).when(tetris).isPaused(); + doReturn(false).when(tetris).isGameOver(); + doReturn(false).when(tetris).isNewGame(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_P); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).setPaused(true); + verify(tetris).getLogicTimer(); + verify(clock).setPaused(tetris.isPaused()); + } + + @Test + public void testPauseKeyPressedPaused() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(true).when(tetris).isPaused(); + doReturn(false).when(tetris).isGameOver(); + doReturn(false).when(tetris).isNewGame(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_P); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).setPaused(false); + verify(tetris).getLogicTimer(); + verify(clock).setPaused(tetris.isPaused()); + } + + @Test + public void testPauseKeyPressedGameOver() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(false).when(tetris).isPaused(); + doReturn(true).when(tetris).isGameOver(); + doReturn(false).when(tetris).isNewGame(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_P); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris, never()).setPaused(true); + verify(tetris, never()).getLogicTimer(); + verify(clock, never()).setPaused(tetris.isPaused()); + } + + @Test + public void testPauseKeyPressedNewGame() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(false).when(tetris).isPaused(); + doReturn(false).when(tetris).isGameOver(); + doReturn(true).when(tetris).isNewGame(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_P); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris, never()).setPaused(true); + verify(tetris, never()).getLogicTimer(); + verify(clock, never()).setPaused(tetris.isPaused()); + } + + @Test + public void testEnterKeyPressed() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(true).when(tetris).isGameOver(); + doReturn(true).when(tetris).isNewGame(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_ENTER); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris).resetGame(); + } + + @Test + public void testEnterKeyPressedDuringGame() { + //given + TetrisKeyAdapter tetrisKeyAdapter = new TetrisKeyAdapter(tetris); + doReturn(false).when(tetris).isGameOver(); + doReturn(false).when(tetris).isNewGame(); + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_ENTER); + + //when + tetrisKeyAdapter.keyPressed(keyEvent); + + //then + verify(tetris, never()).resetGame(); + } + + @Test + public void testDownKeyReleased() { + //given + when(keyEvent.getKeyCode()).thenReturn(KeyEvent.VK_DOWN); + + //when + tetrisKeyAdapter.keyReleased(keyEvent); + + //then + verify(tetris, atLeast(1)).getLogicTimer(); + verify(clock).setCyclesPerSecond(tetris.getGameSpeed()); + verify(tetris, atLeast(1)).getLogicTimer(); + verify(clock).reset(); + } + +} +