Last active
December 12, 2023 07:43
-
-
Save rebekah/268e68920988590cd6e7a947b4d6a964 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tetris; | |
import java.awt.*; | |
import javax.swing.*; | |
import java.awt.event.KeyEvent; | |
import java.awt.event.KeyListener; | |
import java.io.IOException; | |
import java.util.*; | |
import java.lang.Thread; | |
import javax.sound.sampled.*; | |
import java.io.File; | |
import static java.lang.Math.ceil; | |
import static java.lang.String.format; | |
public class Game extends JFrame { | |
JLabel header; | |
JPanel[][] gameSquareArray = new JPanel[20][10]; | |
Integer[][][] shapes = { | |
{{0,5},{-1,5},{-2,5},{-3,5}}, //straight bar | |
{{0,5},{0,6},{-1,6},{-1,7}}, // s shape | |
{{0,6},{0,5},{-1,5},{-1,4}}, // z shape | |
{{0,6},{-1,5},{-1,6},{-2,6}}, //t shape | |
{{0,5},{0,6},{-1,5},{-1,6}}, //block | |
{{0,5},{0,6},{-1,5},{-2,5}}, // l shape | |
{{0,5},{0,6},{-1,6},{-2,6}}, // j shape | |
}; | |
Color[] neons = { | |
new Color(127,255,0), //neon green | |
new Color(238,255,9), //neon yellow | |
new Color(0,255,243), //neon light blue | |
new Color(138,43,226), //neon purple | |
new Color(255,20,47) //neon pink | |
}; | |
static Integer[] rgbBlack = {0,0,0}; | |
int points = 0; | |
int level = 1; | |
int pace = 750; | |
int dropPace = 25; | |
Color BLACK = new Color(0,0,0); | |
static String action = "down"; | |
Integer[][] blocks; | |
int blocksInLevel = 0; | |
private Clip audioClip; | |
//Scanner scanner = new Scanner(System.in); | |
public Game() { | |
setTitle("GridBagLayout Example"); | |
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
setPreferredSize(new Dimension(580, 1100)); | |
// Creating components for the first row | |
header = new JLabel(getHeaderText()); | |
header.setFont(new Font("MV Boli", Font.BOLD, 20)); | |
header.setHorizontalAlignment(JLabel.CENTER); | |
header.setPreferredSize(new Dimension(580, 100)); | |
// Creating components for the second row | |
JPanel panel = new JPanel(new GridLayout(20, 10,1, 1)); | |
panel.setPreferredSize(new Dimension(580, 1000)); | |
for (int i = 0; i < 20; i++) { | |
for (int j = 0; j < 10; j++){ | |
JPanel square = new JPanel(); | |
square.setBackground(new Color(0,0,0)); | |
gameSquareArray[i][j] = square; | |
panel.add(gameSquareArray[i][j]); | |
} | |
} | |
// Creating GridBagLayout and GridBagConstraints | |
GridBagLayout layout = new GridBagLayout(); | |
GridBagConstraints constraints = new GridBagConstraints(); | |
setLayout(layout); | |
// Adding components to the first row | |
constraints.gridx = 0; | |
constraints.gridy = 0; | |
constraints.gridwidth = 1; | |
constraints.gridheight = 1; | |
constraints.weightx = 1.0; | |
constraints.weighty = 0.04; | |
constraints.fill = GridBagConstraints.BOTH; | |
layout.setConstraints(header, constraints); | |
add(header); | |
addKeyListener(new Game.MyKeyListener()); | |
// Adding components to the second row | |
constraints.gridy = 1; | |
constraints.weighty = 0.96; | |
layout.setConstraints(panel, constraints); | |
add(panel); | |
setFocusable(true); | |
pack(); | |
setLocationRelativeTo(null); | |
setVisible(true); | |
} | |
private String getHeaderText(){ | |
return format("Tetris - Level %d - Total Points: %d", level, points); | |
} | |
void play(){ | |
Clip mainSong = playAudio("Sounds/TakeOnMe.wav",-7.0f); | |
while(true){ | |
blocksInLevel++; | |
System.out.println(format("blocks in level: %d", blocksInLevel)); | |
if(blocksInLevel == 10){ | |
level++; | |
points += 100; | |
header.setText(getHeaderText()); | |
blocksInLevel = 0; | |
pace = (int) Math.ceil(pace * 1.2); | |
} | |
int shape_index = new Random().nextInt(7); | |
blocks = shapes[shape_index]; | |
boolean startingPositionClear = true; | |
for(Integer[] block: blocks){ | |
System.out.println("calling isOccupied from play"); | |
if(isOccupied(block[0], block[1])){ | |
startingPositionClear = false; | |
} | |
} | |
if(!(startingPositionClear)){ | |
stopAudio(mainSong); | |
playAudio("Sounds/ArcadeGameOver.wav",-1.0f); | |
header.setText(format("Game Over - Level %d - Total Points: %d", level, points)); | |
break; | |
} | |
//update points - give 5 points for every shape placed - redraw header | |
points += 5; | |
header.setText(getHeaderText()); | |
int rand_index = new Random().nextInt(neons.length); | |
Color color = neons[rand_index]; | |
//place the shapes | |
for(Integer[] block: blocks){ | |
int row = block[0]; | |
int column = block[1]; | |
if(row >= 0) { | |
gameSquareArray[row][column].setBackground(color); | |
//System.out.println(format("trying to set row: %d and column: %d to white", row, column)); | |
} | |
} | |
boolean tryToMove = true; | |
while(tryToMove) { | |
String currentAction = getAction(); | |
System.out.println(format("in user input take action block. The action is: %s", currentAction)); | |
if(currentAction == "rotate"){ | |
if(!(rotateAndDrop())) { | |
Integer[][] proposed = getProposed(blocks, "down"); | |
tryToMove = attemptShift(blocks, proposed); | |
} | |
} else if(currentAction == "right" | currentAction == "left") { | |
attemptShift(blocks, getProposed(blocks, currentAction)); | |
Integer[][] proposed = getProposed(blocks, "down"); | |
tryToMove = attemptShift(blocks, proposed); | |
} else if(currentAction == "drop"){ | |
Clip shwoopClip = playAudio("Sounds/Shwooop.wav",-1.0f); | |
while(tryToMove) { | |
//play drop sound here - the swoosh sound | |
Integer[][] proposed = getProposed(blocks, "down"); | |
tryToMove = attemptShift(blocks, proposed); | |
try { | |
Thread.sleep(dropPace); | |
} catch(Exception e) { | |
System.out.println(e.getMessage()); | |
} | |
} | |
stopAudio(shwoopClip); | |
} else { | |
Integer[][] proposed = getProposed(blocks, "down"); | |
tryToMove = attemptShift(blocks, proposed); | |
} | |
if(!(tryToMove)){ | |
System.out.println("In play going to remove Completed rows."); | |
removeCompletedRows(); | |
} | |
try { | |
Thread.sleep(pace); | |
} catch(Exception e) { | |
System.out.println(e.getMessage()); | |
} | |
} | |
} | |
} | |
public Clip playAudio(String soundFile, Float volume) { | |
try { | |
File audioFile = new File(soundFile); | |
AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile); | |
audioClip = AudioSystem.getClip(); | |
audioClip.open(audioStream); | |
//audioClip.loop(Clip.LOOP_CONTINUOUSLY); | |
audioClip.start(); | |
if (audioClip.isControlSupported(FloatControl.Type.MASTER_GAIN)) { | |
FloatControl gainControl = (FloatControl) audioClip.getControl(FloatControl.Type.MASTER_GAIN); | |
gainControl.setValue(volume); | |
} | |
} catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) { | |
e.printStackTrace(); | |
} | |
return audioClip; | |
} | |
public void stopAudio( Clip soundClip) { | |
if (soundClip != null && soundClip.isRunning()) { | |
soundClip.stop(); | |
soundClip.close(); | |
} | |
} | |
static String getAction(){ | |
String currentAction = action; | |
action = "down"; //reset Action | |
return currentAction; | |
} | |
static void setAction(String capturedAction) { | |
System.out.println(format("USER INPUT: %s", capturedAction)); | |
System.out.println("Updating Action!"); | |
action = capturedAction; | |
} | |
boolean shift(Integer[][] blocks, String direction){ | |
return true; | |
} | |
boolean rotateAndDrop(){ | |
Integer[] rows = Arrays.stream(blocks) | |
.map(block -> block[0]) | |
.toArray(size -> new Integer[size]); | |
Integer[] cols = Arrays.stream(blocks) | |
.map(block -> block[1]) | |
.toArray(size -> new Integer[size]); | |
Arrays.sort(cols); | |
Integer maxCol = cols[cols.length -1]; | |
Integer minCol = cols[0]; | |
Arrays.sort(rows); | |
Integer maxRow = rows[rows.length -1]; | |
Integer minRow = rows[0]; | |
int randVal = new Random().nextInt(2); | |
int halfOfDiffCols = 0; | |
if(randVal == 0) { | |
halfOfDiffCols = (int) Math.floor((maxCol - minCol)/2); | |
} else { | |
halfOfDiffCols = (int) ceil((maxCol - minCol)/2); | |
}; | |
int halfOfDiffRows = (int) ceil((maxRow - minRow)/2); | |
int pivotRow = minRow + halfOfDiffRows; | |
int pivotCol = minCol + halfOfDiffCols; | |
if(pivotRow < 0) { | |
return false; | |
} | |
Integer[][] graphCoordinates = normalize(blocks, pivotRow, pivotCol); | |
Integer[][] rotated = rotate(graphCoordinates); | |
Integer[][] proposed = denormalize(rotated, pivotRow, pivotCol); //denormalize | |
if(!(checkForConflict(proposed, blocks))){ | |
System.out.println("trying to rotate!"); | |
for(Integer [] block: blocks){ | |
System.out.println(format("Prior Current Block: %s", Arrays.toString(block))); | |
} | |
for(Integer[] proposedBlock: proposed) { | |
System.out.println(format("Proposed Block: %s", Arrays.toString(proposedBlock))); | |
} | |
attemptShift(blocks, proposed); | |
for(Integer [] block: blocks){ | |
System.out.println(format("Current Block: %s", Arrays.toString(block))); | |
} | |
return true; | |
} else { | |
return false; | |
} | |
} | |
Integer[][] normalize(Integer[][] blocks, int pivotRow, int pivotCol){ | |
return Arrays.stream(blocks) | |
.map(block -> { | |
int yVal = pivotRow - block[0]; | |
int xVal = block[1] - pivotCol; | |
Integer[] array = {xVal, yVal}; | |
return array; | |
}).toArray(size -> new Integer[size][2]); | |
} | |
Integer[][] rotate(Integer[][] graphCoordinates){ | |
return Arrays.stream(graphCoordinates) | |
.map(coordinate -> { | |
Integer xVal = coordinate[0]; | |
Integer yVal = coordinate[1]; | |
Integer newX; | |
Integer newY; | |
//if(direction == "rotateCW"){ | |
newX = xVal * 0 + yVal * 1; | |
newY = xVal * -1 + yVal * 0; | |
//} else if(direction == "rotateCW") { | |
// newX = xVal * 0 + yVal * -1; | |
// newY = xVal * 1 + yVal * 0; | |
//} | |
return new Integer[]{newX, newY}; | |
}).toArray(size -> new Integer[size][2]); | |
} | |
Integer[][] denormalize(Integer[][] graphCoordinates, int pivotRow, int pivotCol){ | |
return Arrays.stream(graphCoordinates) | |
.map(coordinate -> { | |
int row = pivotRow - coordinate[1]; | |
int col = pivotCol + coordinate[0]; | |
Integer[] array = {row, col}; | |
return array; | |
}).toArray(size -> new Integer[size][2]); | |
} | |
boolean attemptShift(Integer[][] currentBlocks, Integer[][] proposedBlocks){ | |
boolean conflicts = checkForConflict(proposedBlocks, currentBlocks); | |
if(conflicts) { | |
//System.out.println("there were conflicts in attemptShift"); | |
return false; | |
} else { | |
int row = -1; | |
int col = 0; | |
int i = 0; | |
while(row < 0) { | |
row = currentBlocks[i][0]; | |
col = currentBlocks[i][1]; | |
} | |
JPanel panel = gameSquareArray[row][col]; | |
Integer[] rgb = getRGB(panel); | |
Color color = new Color(rgb[0], rgb[1], rgb[2]); | |
blocks = shift(currentBlocks, proposedBlocks, color); | |
return true; | |
} | |
} | |
Integer[][] shift(Integer[][] currentBlocks, Integer[][] newBlocks, Color color){ | |
for(Integer[] block: currentBlocks){ | |
int row = block[0]; | |
int col = block[1]; | |
if(row >= 0) { | |
setPanelColor(row, col, BLACK); | |
} | |
} | |
for(Integer[] block: newBlocks){ | |
int row = block[0]; | |
int col = block[1]; | |
if(row >= 0) { | |
setPanelColor(row, col, color); | |
} | |
} | |
return newBlocks; | |
} | |
void removeCompletedRows(){ | |
ArrayList<Integer> completedRows = new ArrayList<Integer>(); | |
for(Integer[] block: blocks) { | |
int row = block[0]; | |
if(!(completedRows.contains(row))) { | |
boolean completed = true; | |
int rowLength = gameSquareArray[0].length; | |
System.out.println(format("The computer thinks the number of columns is %d", rowLength)); | |
for (int i = 0; i < 10; i++) { | |
System.out.println(format("looking at row %d, and col %d", row, i)); | |
boolean isOccupied = isOccupied(row, i); | |
System.out.println(format("isOccupied: %b", isOccupied)); | |
if (!(isOccupied(row, i))) { | |
completed = false; | |
System.out.println("breaking now"); | |
break; | |
} | |
} | |
if (completed) { | |
System.out.println("Folks we have a complete row!"); | |
completedRows.add(row); | |
} | |
} | |
} | |
for(int row: completedRows) { | |
System.out.println(format("trying to delete row: %d", row)); | |
for (int i = 0; i < 10; i++) { | |
setPanelColor(row, i, BLACK); | |
} | |
} | |
Collections.sort(completedRows, Collections.reverseOrder()); | |
if(completedRows.size() > 0) { | |
Clip twinklesClip = playAudio("Sounds/Twinkles.wav", -1.0f); | |
for (int i = 0; i < completedRows.size(); i++) { | |
dropTo(completedRows.get(i) + i); | |
} | |
stopAudio(twinklesClip); | |
} | |
points = points + 100 * completedRows.size(); | |
header.setText(getHeaderText()); | |
} | |
void dropTo(int row){ | |
Integer[][] current = new Integer[1][2]; | |
Integer[][] below = new Integer[1][2]; | |
for(int i = row -1; i >= 0; i--){ | |
for(int j = 0; j < gameSquareArray[0].length; j++){ | |
System.out.println("calling isOccupied from dropTo"); | |
if(isOccupied(i,j)){ | |
current[0][0] = i; | |
current[0][1] = j; | |
below[0][0] = i + 1; | |
below[0][1] = j; | |
attemptShift(current, below); | |
} | |
} | |
} | |
} | |
void setPanelColor(int row, int col, Color color){ | |
JPanel panel = gameSquareArray[row][col]; | |
panel.setBackground(color); | |
} | |
//check for overlap | |
boolean checkForConflict(Integer[][] proposedBlocks, Integer[][] currentBlocks){ | |
for(Integer[] proposedBlock: proposedBlocks){ | |
boolean notInCurrentBlocks = true; | |
for(Integer[] currentBlock: currentBlocks) { | |
//System.out.println(format( | |
// "proposed_block_row: %d, current_block_row: %d, proposed_block_columns: %d, current_block_column: %d", | |
// proposedBlock[0], | |
// currentBlock[0], | |
// proposedBlock[1], | |
// currentBlock[1] | |
//)); | |
if ((proposedBlock[0] == currentBlock[0]) && (proposedBlock[1] == currentBlock[1])) { | |
notInCurrentBlocks = false; | |
} | |
} | |
int row = proposedBlock[0]; | |
int column = proposedBlock[1]; | |
//System.out.println("calling isOccupied from checkForConflicts"); | |
if(notInCurrentBlocks && isOccupied(row, column)){ | |
//System.out.println("Thinks we are not in current blocks"); | |
return true; | |
} | |
} | |
return false; | |
} | |
Integer[][] getProposed(Integer[][] currentBlocks, String direction) { | |
Integer[][] proposedBlocks = new Integer[4][2]; | |
for( int i = 0; i < 4; i++){ | |
if(direction == "right") { | |
proposedBlocks[i][0] = currentBlocks[i][0]; | |
proposedBlocks[i][1] = currentBlocks[i][1] + 1; | |
} else if(direction == "left"){ | |
proposedBlocks[i][0] = currentBlocks[i][0]; | |
proposedBlocks[i][1] = currentBlocks[i][1] - 1; | |
} else { | |
proposedBlocks[i][0] = currentBlocks[i][0] + 1; | |
proposedBlocks[i][1] = currentBlocks[i][1]; | |
} | |
} | |
return proposedBlocks; | |
} | |
boolean isOccupied(int row, int col){ | |
if(row < 0){ | |
return false; | |
} else if(col < 0 | col > 9 | row > 19) { | |
return true; | |
} else { | |
//System.out.println("where i expect to be in isOccupied"); | |
JPanel proposed = gameSquareArray[row][col]; | |
Integer[] rgb = getRGB(proposed); | |
//System.out.println(format("proposed square - row: %d, col:%d, rgb: %s", row, col, Arrays.toString(rgb))); | |
//System.out.println(format("rgbBlack: %s", Arrays.toString(rgbBlack))); | |
boolean retVal = !(Arrays.equals(rgb, rgbBlack)); | |
//System.out.println(format("return val from isOccupied: %b", retVal)); | |
return retVal; | |
} | |
} | |
Integer[] getRGB(JPanel panel) { | |
int red = panel.getBackground().getRed(); | |
int green = panel.getBackground().getGreen(); | |
int blue = panel.getBackground().getBlue(); | |
Integer[] rgb = {red, green, blue}; | |
return rgb; | |
} | |
private class MyKeyListener implements KeyListener { | |
@Override | |
public void keyTyped(KeyEvent e) { | |
// Not used in this example | |
} | |
@Override | |
public void keyPressed(KeyEvent e) { | |
// Change the color of each panel on a key press | |
} | |
@Override | |
public void keyReleased(KeyEvent e) { | |
int keyCode = e.getKeyCode(); | |
System.out.println(format("the keycode is: %d", keyCode)); | |
String inputedAction = "down"; | |
if(keyCode == 37) { | |
inputedAction = "left"; | |
} else if(keyCode == 38) { | |
inputedAction = "rotate"; | |
} else if(keyCode == 39){ | |
inputedAction = "right"; | |
} else if(keyCode == 40) { | |
inputedAction = "drop"; | |
} | |
setAction(inputedAction); | |
} | |
} | |
public static void main(String[] args) { | |
Game game = new Game(); | |
game.requestFocusInWindow(); | |
game.play(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment