Skip to content

Instantly share code, notes, and snippets.

@rebekah
Last active December 12, 2023 07:43
Show Gist options
  • Save rebekah/268e68920988590cd6e7a947b4d6a964 to your computer and use it in GitHub Desktop.
Save rebekah/268e68920988590cd6e7a947b4d6a964 to your computer and use it in GitHub Desktop.
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