I'm having some problems with Tetris. So first of all, I have a class Shape, and then subclasses of Shape for each shape type. This is how a Shape subclass looks like:
public class SquareShape extends Shape {
public SquareShape(){
coords = squareShapeCoords;
shape = SQUARESHAPE;
}
}
In the Shape class I have a rotate method as follows:
protected void setX(int i, int x) { coords[i][0] = x; }
protected void setY(int i, int y) { coords[i][1] = y; }
public int x(int i) { return coords[i][0]; }
public int y(int i) { return coords[i][1]; }
public Shape rotate(){
if (this.getShape().equals(SQUARESHAPE)) return this;
Shape newShape = new Shape();
newShape.shape = this.shape;
for (int i = 0; i < 4; i++) {
newShape.setX(i, y(i));
newShape.setY(i, -x(i));
}
return newShape;
}
Note that I store the coordinates of each shape in 2D arrays.
Also, this is my game engine class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GameEngine extends JPanel implements ActionListener{
private final int HEIGHT = 15;
private final int WIDTH = 10;
private int score;
int coordX = 0;
int coordY = 0;
Timer timer;
boolean isFinishedFalling = false;
boolean isRunning = false;
boolean isPaused = false;
Shape block;
String[] shapes;
public GameEngine(){
setFocusable(true);
block = new Shape();
timer = new Timer(600, this);
timer.start();
addMouseListener(new MAdapter());
addMouseWheelListener(new WheelAdapter());
addKeyListener(new KAdapter());
setBackground(Color.BLACK);
shapes = new String[WIDTH * HEIGHT];
clearShapes();
}
int squareWidth() { return (int) getSize().getWidth() / WIDTH; }
int squareHeight() { return (int) getSize().getHeight() / HEIGHT; }
String shapeAt(int x, int y) { return shapes[(y * WIDTH) + x]; }
public int getScore() {return score;}
public void actionPerformed(ActionEvent e){
if(isFinishedFalling){
isFinishedFalling = false;
newBlock();
} else moveDown();
}
private boolean move(Shape newShape, int newCoordX, int newCoordY)
{
for (int i = 0; i < 4; ++i) {
int x = newCoordX + newShape.x(i);
int y = newCoordY - newShape.y(i);
if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return false;
if (!shapeAt(x, y).equals(Shape.DEFAULTSHAPE)) return false;
}
block = newShape;
coordX = newCoordX;
coordY = newCoordY;
repaint();
return true;
}
private boolean moveLeft() { return move(block, coordX-1, coordY);}
private boolean moveRight() { return move(block, coordX+1, coordY);}
private boolean moveDown(){
if(!move(block, coordX, coordY-1)){
blockIsDown();
return false;
} else return true;
}
private void dropDown(){
int y = coordY;
while(y>0){
if(!move(block, coordX, y-1)) break;
y -= 1;
}
blockIsDown();
}
private boolean rotate() { return move(block.rotate(), coordX, coordY);}
private void blockIsDown(){
for(int i=0; i<4; i++){
int a = coordX + block.x(i);
int b = coordY - block.y(i);
shapes[b * WIDTH + a] = block.getShape();
}
clearFullLines();
if(!isFinishedFalling) newBlock();
}
private void clearFullLines(){
int fullLines = 0;
for(int i = HEIGHT-1; i>=0; i--){
boolean lineFull = true;
for(int j=0; j<WIDTH; j++){
if(shapeAt(j, i).equals(Shape.DEFAULTSHAPE)){
lineFull = false;
break;
}
}
if(lineFull){
fullLines++;
for(int m=i; m<HEIGHT-1; m++){
for(int n=0; n<WIDTH; n++)
shapes[(m*WIDTH) + n] = shapeAt(n, m+1);
}
}
}
if(fullLines>0){
score += fullLines*100;
isFinishedFalling = true;
block.setShape(Shape.DEFAULTSHAPE);
repaint();
}
}
private void newBlock()
{
block = new RandomShape();
coordX = WIDTH / 2 + 1;
coordY = HEIGHT - 1 + block.minY();
if (!move(block, coordX, coordY)) {
block.setShape(Shape.DEFAULTSHAPE);
timer.stop();
isRunning = false;
}
}
private void clearShapes(){
for(int i=0; i< WIDTH * HEIGHT; i++) shapes[i] = Shape.DEFAULTSHAPE;
}
private void drawSquare(Graphics g, int x, int y, String shape){
Color color = Color.BLACK;
if(shape.equals(Shape.ZSHAPE)) color = Color.GREEN;
if(shape.equals(Shape.SSHAPE)) color = Color.RED;
if(shape.equals(Shape.LINESHAPE)) color = Color.CYAN;
if(shape.equals(Shape.TSHAPE)) color = Color.BLUE;
if(shape.equals(Shape.SQUARESHAPE)) color = Color.YELLOW;
if(shape.equals(Shape.LSHAPE)) color = Color.MAGENTA;
if(shape.equals(Shape.MIRROREDLSHAPE)) color = Color.ORANGE;
g.setColor(color);
g.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2);
g.setColor(color.brighter());
g.drawLine(x, y + squareHeight() - 1, x, y);
g.drawLine(x, y, x + squareWidth() - 1, y);
g.setColor(color.darker());
g.drawLine(x + 1, y + squareHeight() - 1, x + squareWidth() - 1, y + squareHeight() - 1);
g.drawLine(x + squareWidth() - 1, y + squareHeight() - 1, x + squareWidth() - 1, y + 1);
}
public void paint(Graphics g){
super.paint(g);
Dimension size = getSize();
int top = (int) size.getHeight() - HEIGHT * squareHeight();
for(int i=0; i<HEIGHT; i++){
for(int j=0; j<WIDTH; j++){
String s = shapeAt(j, HEIGHT-i-1);
if(!s.equals(Shape.DEFAULTSHAPE))
drawSquare(g, j * squareWidth(), top + i * squareHeight(), s);
}
}
if(!block.getShape().equals(Shape.DEFAULTSHAPE)){
for(int i=0; i<4; i++){
int x = coordX + block.x(i);
int y = coordY - block.y(i);
drawSquare(g, x * squareWidth(), top + (HEIGHT - y - 1) * squareHeight(), block.getShape());
}
}
}
public void start(){
if(isPaused) return;
isRunning = true;
isFinishedFalling = false;
score = 0;
clearShapes();
newBlock();
timer.start();
}
private void pause(){
if(!isRunning) return;
isPaused = !isPaused;
if(isPaused){
timer.stop();
} else{
timer.start();
}
repaint();
}
class MAdapter extends MouseAdapter{
public void mouseClicked(MouseEvent e){
if (!isRunning || block.getShape().equals(Shape.DEFAULTSHAPE) || isPaused) return;
int buttonPressed = e.getButton();
if(buttonPressed == MouseEvent.BUTTON1) moveLeft();
if(buttonPressed == MouseEvent.BUTTON2) rotate();
if(buttonPressed == MouseEvent.BUTTON3) moveRight();
}
}
class WheelAdapter implements MouseWheelListener{
public void mouseWheelMoved(MouseWheelEvent e){
if (!isRunning || block.getShape().equals(Shape.DEFAULTSHAPE) || isPaused) return;
int wheelRotation = e.getWheelRotation();
if(wheelRotation == 1) moveDown();
if(wheelRotation == -1) dropDown();
}
}
class KAdapter extends KeyAdapter{
public void keyPressed(KeyEvent e){
if (!isRunning || block.getShape().equals(Shape.DEFAULTSHAPE) || isPaused) return;
int key = e.getKeyCode();
if(key == KeyEvent.VK_SPACE) pause();
}
}
}
My problem is the following: When I try to rotate the blocks, it works good the first time, but it messes up completely if I try to rotate them a second time.
This is supposed to be my line shape:
And this is my L shape (the yellow one):
Note that this isn't just a graphical bug, the game treats the elements as one single square, or two respectively.
I've been looking for hours at my code to see what the problem could be, but I had no luck. Any help would be appreciated
Thank you for the replies, but after further investigation, I found out what the problem was.
The problem was with the rotate method:
public Shape rotate(){
if (this.getShape().equals(SQUARESHAPE)) return this;
Shape newShape = new Shape();
newShape.shape = this.shape;
for (int i = 0; i < 4; i++) {
newShape.setX(i, -y(i));
newShape.setY(i, x(i));
}
return newShape;
}
I added to the mouse adapter the following code to see what happens with the coordinates of the current block:
if(buttonPressed == MouseEvent.BUTTON2) {
System.out.println(Arrays.deepToString(block.getCoords()));
rotate();
}
This is the output for the SShape:
[[0, -1], [0, 0], [1, 0], [1, 1]]
[[1, 0], [0, 0], [0, 1], [-1, 1]]
[[0, 0], [0, 0], [-1, -1], [-1, -1]]
[[0, 0], [0, 0], [1, 1], [1, 1]]
[[0, 0], [0, 0], [-1, -1], [-1, -1]]
[[0, 0], [0, 0], [1, 1], [1, 1]]
[[0, 0], [0, 0], [-1, -1], [-1, -1]]
The first line contains the initial coordinates I gave for the SShape. The second line contains the modified coordinates, after the rotate() method. As you can see, X takes the -Y value and Y takes the X value. In the third line however, X takes the -Y value, but Y takes the updated X value instead of the previous one, so X = Y from the third line onwards. To solve this problem, I made an array to hold the values of X before updating it, as follows:
public Shape rotate(){
if (this.getShape().equals(SQUARESHAPE)) return this;
Shape newShape = new Shape();
newShape.shape = this.shape;
int[] oldX = {this.x(0), this.x(1), this.x(2), this.x(3)};
for (int i = 0; i < 4; i++) {
newShape.setX(i, -y(i));
newShape.setY(i, oldX[i]);
}
return newShape;
}
Remove the rotate method, add a hard-coded rotation array each shape and all 4 rotations. First index should be the rotation index (0-3)
then add a member variable to the shape base class and change rotation to something like:
public void rotate(Boolean rotateRight) {
if (rotateRight) {
rotation++;
else {
rotation--;
}
if (rotation < 0) {
rotation = 3;
}
if (rotation > 3) {
rotation = 0;
}
}
Then make some new function like
public int[][] getCurrentRotation() {
return shapeMatrix[rotation];
}
and use this int[][] (or int[] if you want to flatten the array) to draw the appropriate squares
Related
Hello I am relatively new to java and I am writing a tetris style program. I am currently getting a nullpointerException like this :
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at tetris2.GameBoard.drawSquare(Tetris.java:359)
at tetris2.GameBoard.paint(Tetris.java:250)
I have marked out the lines where the exception is pointing to. I understand what a NulPointerException is but I cant work out what class that I have referenced without creating
here is my code:
public class Tetris {
public static void createGUI()
{
final JFrame frame = new JFrame("159.235 - A2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel contentPane = new JPanel(new BorderLayout());
frame.setContentPane(contentPane);
final GameBoard gameBoard = new GameBoard();
contentPane.add(gameBoard, BorderLayout.CENTER);
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e)
{
gameBoard.keyTyped(e);
}
#Override
public void keyReleased(KeyEvent e)
{
gameBoard.keyReleased(e);
}
#Override
public void keyPressed(KeyEvent e)
{
switch (e.getKeyChar()) {
case KeyEvent.VK_ESCAPE:
gameBoard.pauseGame();
System.exit(0);
break;
default:
gameBoard.keyPressed(e);
}
}
});
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
// Note: you might want to add a button to start, pause, or resume the
// game instead of automatically starting it here
gameBoard.startGame();
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run()
{
createGUI();
}
});
}
}
class GameBoard extends JPanel implements KeyListener {
private static final long serialVersionUID = 1L;
// the number of rows and columns on the game board
public static final int NUM_COLS = 10;
public static final int NUM_ROWS = 20;
// the size of each cell in pixels
public static final int CELL_SIZE = 20;
// the game board size in pixels
public final int GAME_FIELD_WIDTH = NUM_COLS * CELL_SIZE;
public final int GAME_FIELD_HEIGHT = NUM_ROWS * CELL_SIZE;
// the interval between game state updates
private int m_updateInterval = 500;
// the game timer initiates an update to the game state
private final Timer m_gameTimer = new Timer(m_updateInterval,
new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
if(isFallingFinished) {
isFallingFinished = false;
newPiece();;
} else {
oneLineDown();
}
}
});
// the game board, with [0][0] being the bottom left cell
//private final Block[][] m_cells = new Block[NUM_ROWS][NUM_COLS];
// the currently active shape
private Shapes m_currentShape = new Shapes();
public int curX = 0;
public int curY = 0;
tetrisPieces[] board;
boolean isFallingFinished = false;
boolean isStarted = false;
boolean isPaused = false;
int numLinesRemoved = 0;
// ////////////////////////////////////////////////////////////////////
public GameBoard()
{
setMinimumSize(new Dimension(GAME_FIELD_WIDTH + 1, GAME_FIELD_HEIGHT));
setPreferredSize(new Dimension(GAME_FIELD_WIDTH + 1, GAME_FIELD_HEIGHT));
setOpaque(true);
// set-up the timer for the render loop
m_gameTimer.setInitialDelay(m_updateInterval);
m_gameTimer.setRepeats(true);
board = new tetrisPieces[GAME_FIELD_WIDTH * GAME_FIELD_HEIGHT];
clearBoard();
newPiece();
}
int squareWidth() { return (int) getSize().getWidth() / GAME_FIELD_WIDTH;}
int squareHeight() { return (int) getSize().getHeight() / GAME_FIELD_HEIGHT; }
tetrisPieces shapeAt(int x, int y) { return board[(y * GAME_FIELD_WIDTH) + x]; }
#Override
public void keyPressed(KeyEvent e)
{
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
m_currentShape.rotateShape();
break;
case KeyEvent.VK_DOWN: // move down
oneLineDown();
break;
case KeyEvent.VK_LEFT: // move left
tryMove(m_currentShape, curX - 1, curY);
break;
case KeyEvent.VK_RIGHT: // move right
tryMove(m_currentShape, curX + 1, curY);
break;
case KeyEvent.VK_SPACE: // toggle pause / resume
if (m_gameTimer.isRunning()) pauseGame();
else startGame();
break;
}
}
#Override
public void keyReleased(KeyEvent e)
{
switch (e.getKeyCode()) {
case KeyEvent.VK_DOWN: // disable down key
break;
case KeyEvent.VK_LEFT: // disable left key
break;
case KeyEvent.VK_RIGHT: // disable right key
break;
}
}
#Override
public void keyTyped(KeyEvent e)
{}
public void startGame()
{
if(isPaused)
return;
isStarted = true;
isFallingFinished = false;
numLinesRemoved = 0;
clearBoard();
board = new tetrisPieces[getWidth() * getHeight()];
newPiece();
m_gameTimer.start();
}
public void pauseGame()
{
if (!isStarted)
return;
isPaused = !isPaused;
if(isPaused) {
m_gameTimer.stop();
//statusbar.setText("paused");
} else {
m_gameTimer.start();
//statusbar.setText(String.valueOf(numLinesRemoved));
}
repaint();
}
public void paint(Graphics g)
{
super.paint(g);
Dimension size = getSize();
int boardTop = (int) size.getHeight() - GAME_FIELD_HEIGHT * squareHeight();
for (int i = 0; i < GAME_FIELD_HEIGHT; ++i) {
for (int j = 0; j < GAME_FIELD_WIDTH; ++j) {
tetrisPieces shapes = shapeAt(j, GAME_FIELD_HEIGHT - i -1);
if (shapes != tetrisPieces.noPiece)
drawSquare(g, 0 + j * squareWidth(),boardTop + i * squareHeight(), shapes);
//**This line above**
}
}
if(m_currentShape.getShape() != tetrisPieces.noPiece) {
for(int i = 0; i <4; ++i) {
int x = curX + + m_currentShape.x(i);
int y = curY - m_currentShape.y(i);
drawSquare(g, 0 + x * squareWidth(),
boardTop + (GAME_FIELD_HEIGHT - y - 1) * squareHeight(),
m_currentShape.getShape());
}
}
}
private void oneLineDown()
{
if(!tryMove(m_currentShape, curX, curY - 1))
pieceDropped();
}
private void clearBoard() {
for (int i = 0; i <GAME_FIELD_HEIGHT * GAME_FIELD_WIDTH; ++i)
board[i] = tetrisPieces.noPiece;
}
private boolean tryMove(Shapes newPiece, int newX, int newY) {
for(int i = 0; i< 4; ++i) {
int x = newX + newPiece.x(i);
int y = newY - newPiece.y(i);
if( x < 0 || x >= GAME_FIELD_WIDTH || y < 0 || y >= GAME_FIELD_HEIGHT)
return false;
if(shapeAt(x, y) != tetrisPieces.noPiece)
return false;
}
m_currentShape = newPiece;
curX = newX;
curY = newY;
repaint();
return true;
}
private void pieceDropped() {
for (int i =0; i < 4; ++i) {
int x = curX + m_currentShape.x(i);
int y = curY - m_currentShape.y(i);
board[(y * GAME_FIELD_WIDTH) + x] = m_currentShape.getShape();
}
removeFullLines();
if(!isFallingFinished)
newPiece();
}
private void removeFullLines() {
int numFullLines = 0;
for(int i = GAME_FIELD_HEIGHT -1; i >= 0; --i) {
boolean lineIsFull = true;
for(int j = 0; j < GAME_FIELD_WIDTH; ++j) {
if(shapeAt(j, i) == tetrisPieces.noPiece) {
lineIsFull = false;
break;
}
}
if(lineIsFull) {
++numFullLines;
for(int k = i; k < GAME_FIELD_HEIGHT - 1; ++k) {
for(int j = 0; j < GAME_FIELD_WIDTH; ++j)
board[(k* GAME_FIELD_WIDTH) + j] = shapeAt(j, k + 1);
}
}
}
}
private void newPiece() {
m_currentShape.setRandomShape();
curX = GAME_FIELD_WIDTH / 2 + 1;
curY = GAME_FIELD_HEIGHT - 1 + m_currentShape.minY();
if(!tryMove(m_currentShape, curX, curY)) {
m_currentShape.setShape(tetrisPieces.noPiece);
m_gameTimer.stop();
isStarted = false;
}
}
private void drawSquare(Graphics g, int x, int y, tetrisPieces shape)
{
Color colors[] = { new Color(0, 0, 0), new Color(204, 102, 102),
new Color(102, 204, 102), new Color(102, 102, 204),
new Color(204, 204, 102), new Color(204, 102, 204),
new Color(102, 204, 204), new Color(218, 170, 0)
};
Color color = colors[shape.ordinal()]; //**This line here**
g.setColor(color);
g.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2);
g.setColor(color.brighter());
g.drawLine(x, y + squareHeight() - 1, x, y);
g.drawLine(x, y, x + squareWidth() - 1, y);
g.setColor(color.darker());
g.drawLine(x + 1, y + squareHeight() - 1,
x + squareWidth() - 1, y + squareHeight() - 1);
g.drawLine(x + squareWidth() - 1, y + squareHeight() - 1,
x + squareWidth() - 1, y + 1);
}
}
The Shapes class:
public class Shapes {
enum tetrisPieces { noPiece, ZShape, SSHape, TShape, LShape, SquareShape,
ReverseLShape}
private tetrisPieces pieceShape;
private int coords[][];
private int coordsTable[][][];
public Shapes() {
coords = new int[4][2];
setShape(tetrisPieces.noPiece);
}
public void setShape(tetrisPieces shape) {
coordsTable = new int[][][] {
{ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
{ { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, 1 } },
{ { 0, -1 }, { 0, 0 }, { 1, 0 }, { 1, 1 } },
{ { 0, -1 }, { 0, 0 }, { 0, 1 }, { 0, 2 } },
{ { -1, 0 }, { 0, 0 }, { 1, 0 }, { 0, 1 } },
{ { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } },
{ { -1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } },
{ { 1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } }
};
for(int i = 0; i < 4; i++) {
for(int j = 0; j<2; ++j) {
coords[i][j] = coordsTable[shape.ordinal()][i][j];
}
}
pieceShape = shape;
}
private void setX(int index, int x) { coords[index][0] = x; }
private void setY(int index, int y) { coords[index][1] = y; }
public int x(int index) { return coords[index][0]; }
public int y(int index) { return coords[index][1]; }
public tetrisPieces getShape() { return pieceShape; }
public void setRandomShape()
{
Random r = new Random();
int x = Math.abs(r.nextInt()) % 6 + 1;
tetrisPieces[] values = tetrisPieces.values();
setShape(values[x]);
}
public int minX() {
int m =coords[0][0];
for (int i = 0; i < 4; i++) {
m = Math.min(m, coords[i][0]);
}
return m;
}
public int minY() {
int m = coords[0][1];
for (int i =0; i <4; i++) {
m = Math.min(m, coords[i][1]);
}
return m;
}
public Shapes rotateShape() {
if(pieceShape == tetrisPieces.SquareShape)
return this;
Shapes result = new Shapes();
result.pieceShape = pieceShape;
for(int i = 0; i <4; ++i) {
result.setX(i, -y(i));
result.setY(i, x(i));
}
return result;
}
}
Color color = colors[shape.ordinal()];
The only thing that can be null at this line is shape, since colors is iniyialized the line before.
Where does shape come from? From the line
tetrisPieces shapes = shapeAt(j, GAME_FIELD_HEIGHT - i -1);
What does shapeAt() return?
tetrisPieces shapeAt(int x, int y) { return board[(y * GAME_FIELD_WIDTH) + x]; }
Have you initialized the elements of the board array before this line is executed? Probably not. The debugger will confirm that. What is probably buggy is these lines:
clearBoard();
board = new tetrisPieces[getWidth() * getHeight()];
The first one initializes every element of the board array, but the second throws away the initialized board array and replaces it by a new, empty one.
I'm trying to write an algorithm to satisfy this challenge. I've double, triple, and quadruple checked my logic, but I think I'm missing something obvious. This program should group each color next to similar colors, but it produces something more akin to noise.
This is sort of what I expect (taken from a similar answer):
And this is what I'm actually getting:
public class AllColors extends JFrame {
private static final int WIDTH = 256;
private static final int HEIGHT = 128;
private static long TOTAL_ITERATIONS = (WIDTH * HEIGHT) * 185000;
private static int VALUES_PER_CHANNEL =32;
private static int CHANNEL_DELTA = 256/VALUES_PER_CHANNEL;
static BufferedImage image;
private static final int SCALE = 5;
static int[][] kernel = { { 0, 0, 1, 0, 0 },
{ 0, 2, 3, 2, 0 },
{ 1, 3, 0, 3, 1 },
{ 0, 2, 3, 2, 0 },
{ 0, 0, 1, 0, 0 } };
public static void main(String[] args) {
AllColors f = new AllColors();
f.setTitle("All Colors");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
image = new BufferedImage(WIDTH * SCALE, HEIGHT * SCALE, BufferedImage.TYPE_3BYTE_BGR);
init();
//gui stuff
JPanel p = new JPanel(){
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.scale(SCALE, SCALE);
g2.drawImage(image, 0, 0, null);
}
};
p.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
f.add(p);
f.pack();
f.setVisible(true);
group(p);
}
//makes an image of all colors
private static void init() {
int x = 0;
int y = 0;
for(int r = 0; r < VALUES_PER_CHANNEL; r+= 1){
for(int g = 0; g < VALUES_PER_CHANNEL; g+= 1){
for(int b = 0; b < VALUES_PER_CHANNEL; b+= 1){
x++;
if(x % WIDTH == 0){
y++;
x = 0;
}
if(y >= HEIGHT)
return;
image.setRGB(x, y, new Color(r*CHANNEL_DELTA,g*CHANNEL_DELTA,b*CHANNEL_DELTA).getRGB());
}
}
}
}
//group together similar pixels
private static void group(JPanel panel){
Random rand = new Random();
for(long i = 0; i < TOTAL_ITERATIONS; i++){
Point first = new Point(rand.nextInt(WIDTH), rand.nextInt(HEIGHT));
Point second = new Point(rand.nextInt(WIDTH), rand.nextInt(HEIGHT));
trySwitch(first, second);
if(i % (WIDTH * HEIGHT) == 0){
System.out.println(i / (WIDTH * HEIGHT));
panel.repaint();
}
}
}
private static void swap(Point first, Point second){
int temp = image.getRGB(second.x, second.y);
image.setRGB(second.x, second.y, image.getRGB(first.x, first.y));
image.setRGB(first.x, first.y, temp);
}
//get how similar the neighbors are
private static int getNeighborDelta(int imageX, int imageY){
Color center = new Color(image.getRGB(imageX, imageY));
int sum = 0;
for (int x = 0; x < kernel[0].length; x++)
{
for (int y = 0; y < kernel.length; y++)
{
int weight = kernel[x][y];
if (weight <= 0)
{
continue;
}
int xOffset = x - (kernel[0].length / 2);
int yOffset = y - (kernel.length / 2);
try{
sum += getDistance(new Color(image.getRGB(imageX + xOffset, imageY + yOffset)), center) * weight;
}catch(ArrayIndexOutOfBoundsException e){
//if out of image
}
}
}
return sum;
}
//switches if the neighbors will be more similar
private static void trySwitch(Point first, Point second){
double firstDistance = getNeighborDelta(first.x, first.y);
swap(first, second);
double secondDistance = getNeighborDelta(first.x, first.y);
if(secondDistance > firstDistance)
swap(first, second);
}
//get similarity between colors
private static double getDistance(Color one, Color two){
int r = Math.abs(two.getRed() - one.getRed());
int g = Math.abs(two.getGreen() - one.getGreen());
int b = Math.abs(two.getBlue() - one.getBlue());
return r + g + b;
}
}
I am developing game using canvas. I have drawn grid of circles using loop. I want to get x,y coordinates of circles. For that I have to make those circles clickable so that whenever player click on a circle it should return its coordinates. I will pass those coordinates to lineDraw() method for drawing line between those circles.
This loop is defining grid of circles:
for (int y=0;y<rows;y++)
{
for (int x=0;x<cols;x++)
{
canvas.drawCircle((x + 1) * dw, (y + 1) *(3* dh), 20, pDot);
}
}
Here dw is (displayWidth) and dh is (displayHeight). Please suggest how I should make these circles clickable?
I had played with this project a little after your previous questions. I'm sure this isn't the most optimized way to do this, but without knowing how you plan to implement the game logic, I think this is flexible enough to adapt. The following is your custom View class, which I've renamed Board, in keeping with Java naming conventions.
public class Board extends View
{
Paint pBack = new Paint();
Paint pDot = new Paint();
Paint pLine = new Paint();
int cols = 5;
int rows = 6;
// Default initialization = false
boolean[][] dots = new boolean[cols][rows];
int canWidth = 0;
int canHeight = 0;
float xStep = 0;
float yStep = 0;
float[] xCoords = new float[cols];
float[] yCoords = new float[rows];
public Board(Context context)
{
super(context);
pBack.setARGB(255, 255, 102, 0);
pDot.setARGB(255, 255, 255, 255);
pLine.setStrokeWidth(5);
pLine.setARGB(255, 90, 10, 0);
}
public void setDots(boolean[][] dotSelected)
{
this.dots = dotSelected;
}
public boolean[][] getDots()
{
return dots;
}
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
canWidth = w;
canHeight = h;
xStep = canWidth / (cols + 1);
yStep = canHeight / (rows + 1);
for (int y = 0; y < rows; y++)
{
yCoords[y] = (y + 1) * yStep;
}
for (int x = 0; x < cols; x++)
{
xCoords[x] = (x + 1) * xStep;
}
}
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawPaint(pBack);
// Grid, lines and box markings
for (int y = 0; y < rows; y++)
{
canvas.drawLine(xStep, yCoords[y], cols * xStep, yCoords[y], pDot);
for (int x = 0; x < cols; x++)
{
if (y == 0)
{
canvas.drawLine(xCoords[x], yStep, xCoords[x], rows * yStep, pDot);
}
if (dots[x][y])
{
boolean left = x > 0 && dots[x - 1][y];
boolean up = y > 0 && dots[x][y - 1];
if (left)
{
canvas.drawLine(xCoords[x], yCoords[y], xCoords[x - 1], yCoords[y], pLine);
}
if (up)
{
canvas.drawLine(xCoords[x], yCoords[y], xCoords[x], yCoords[y - 1], pLine);
}
if (left && up && dots[x - 1][y - 1])
{
canvas.drawCircle(xCoords[x] - xStep / 2, yCoords[y] - yStep / 2, 10, pLine);
}
}
}
}
// Dots
for (int y = 0; y < rows; y++)
{
for (int x = 0; x < cols; x++)
{
canvas.drawCircle(xCoords[x], yCoords[y], 20, pDot);
if (dots[x][y])
{
canvas.drawCircle(xCoords[x], yCoords[y], 15, pBack);
}
}
}
}
public boolean onTouchEvent(MotionEvent event)
{
super.onTouchEvent(event);
if (event.getAction() != MotionEvent.ACTION_DOWN)
return true;
int xNear = 0;
int yNear = 0;
float xMin = canWidth;
float yMin = canHeight;
for (int x = 0; x < cols; x++)
{
if (Math.abs(xCoords[x] - event.getX()) < xMin)
{
xMin = Math.abs(xCoords[x] - event.getX());
xNear = x;
}
}
for (int y = 0; y < rows; y++)
{
if (Math.abs(yCoords[y] - event.getY()) < yMin)
{
yMin = Math.abs(yCoords[y] - event.getY());
yNear = y;
}
}
dots[xNear][yNear] = !dots[xNear][yNear];
invalidate();
return true;
}
}
Store the data of your circles (for instance the position and the radius) in a list and check each of them for mouse collision(register a mouseeventlistener).
public class Circle
{
private int radius;
private int positionX;
private int positionY;
public Circle(int positionX, int positionY, int radius)
{
this.radius = radius;
this.positionX = positionX;
this.positionY = positionY;
}
public int getPositionX()
{
return this.positionX;
}
public int getPositionY()
{
return this.positionY;
}
public boolean contains(int posX, int posY)
{
int distanceX = this.positionX - posX;
int distanceY = this.positionY - posY;
return Math.sqrt((distanceX * distanceX) + (distanceY * distanceY)) <= this.radius;
}
I'm making Tetris in java for fun... I pretty much had everything working... but later found out that when I wanted to change the dimensions so it was square ([10 row][10 col] matrix, but instead a [12 row][10 col] matrix), that I started getting Index Out of Bound exceptions... see here: Java Tetris - weird row clearing issue
So I tried fixing everything so that the rows and columns weren't flip flopped... But am now getting hung up on the fact that the grid takes [row][col], but I’m moving around the tiles as (x, y) coordinates…
What’s confusing me is that row = y and col = x… which is reversed… so when I pass in coordinates I’m not sure when to swap them.
I know it’s a simple thing, but it’s confusing the hell out of me and I keep getting out of bounds exceptions whenever I think I have it right.
I'm not sure where the exact issue is, so I'm posting a full Sscce of my program... I think the issue is in the Board class...
Here, the block should still be able to move down... but if it tries to go down further than this...
I get:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 10
at Board.getTileAt(Board.java:177)
at Tile.collision(Tile.java:31)
at Piece.isCollision(Piece.java:172)
at Board.collisionCheck(Board.java:192)
at Piece.movePieceCheck(Piece.java:87)
at Board.keyPressed(Board.java:160)
Sscce:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MainSscce extends JPanel {
static MainSscce runMe;
BoardSscce gameBoard, scoreBoard;
public MainSscce() { //creates a new frame window and sets properties
JFrame f = new JFrame("Tetris");
//width (height), length, tilesize
gameBoard = new BoardSscce(12, 10, 35);
// scoreBoard = new BoardSscce(10, 10, 35);
f.add(gameBoard);
f.setSize(gameBoard.getWidth(), gameBoard.getHeight());
f.setVisible(true);
f.setResizable(false);
f.setVisible(true);
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
//set j frame location to appear in middle of screen
f.setLocation( (screensize.width - f.getWidth())/2,
(screensize.height - f.getHeight())/2-100 );
}
public static void main(String[] args) {
runMe = new MainSscce();
}
}
import java.awt.Graphics;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.event.*; // for ActionListener and ActionEvent
import java.util.Random;
public class BoardSscce extends JPanel implements KeyListener {
private TileSscce grid[][];
private int totalRows, totalCols, tilesize, level, totalScore;
private final int changeLevelMultiplier;
private PieceSscce newPiece, nextPiece;
private String randomPiece;
private boolean gameLost;
public BoardSscce(int r, int c, int ts) {
totalRows = r;
totalCols = c;
tilesize = ts;
//set grid size to [# rows][# columns], aka [height][width]
grid = new TileSscce[totalRows][totalCols];
gameLost = false;
System.out.println("TotalRows: " + totalRows + ", " + "TotalCols: " + totalCols);
//multiplier to determine what score the level changes, which is:
//level * changeLevelMultiplier;
changeLevelMultiplier = 40;
//initialize score to 0
totalScore = 0;
//initialize level to 0
level = 0;
newPiece = new PieceSscce(this, randomPiece(), getColor());
addKeyListener(this);
setFocusable(true);
//getTranspose();
timer();
}
public String randomPiece() {
String[] Pieces = {"L", "O", "Z", "RevZ", "Bar", "T", "RevL"};
int rand = (int) (Math.random() * Pieces.length);
randomPiece = Pieces[rand];
return randomPiece;
}
public Color getColor() {
Color color;
if (randomPiece.equals("L"))
color = new Color(17, 255, 0);
else if(randomPiece.equals("O"))
color = new Color(117, 168, 255);
else if(randomPiece.equals("Z"))
color = new Color(255, 187, 82);
else if(randomPiece.equals("RevZ"))
color = new Color(206, 27, 72);
else if(randomPiece.equals("Bar"))
color = new Color(50, 216, 219);
else if(randomPiece.equals("T"))
color = new Color(252, 148, 240);
else
color = new Color(255, 255, 52);
//Random rand = new Random();
//float r = rand.nextFloat();
//float g = rand.nextFloat();
//float b = rand.nextFloat();
//Color randomColor = new Color(r, g, b);
return color;
}
//dimensions of board = width * tilesize
public int getWidth() {
return totalCols * tilesize;
}
public int getHeight() {
return totalRows * tilesize;
}
public int getTileSize() {
return tilesize;
}
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
for(int row = 0; row < grid.length; row++) {
for(int col = 0; col < grid[row].length; col++) {
//System.out.println(row + ", " + col);
g.drawString("[" + row + "][" + col + "]", col * tilesize, row * tilesize+10);
System.out.println(row + ", " + col);
//if there is a non-null space, that is a Tetris piece... fill it
if(grid[row][col] != null) {
g.setColor(grid[row][col].getColor());
g.fillRect(row * tilesize, col * tilesize, tilesize, tilesize);
g.setColor(Color.WHITE);
}
}
}
// g.drawString("Level: " + level, this.getWidth()/2, this.getHeight()/2-130);
// g.drawString("Score: " + totalScore, this.getWidth()/2, this.getHeight()/2-100);
if (gameLost == true) {
g.drawString("Way to go, loser...", this.getWidth()/2, this.getHeight()/2);
messageTimer();
}
}
//Auto move piece
public void timer () {
int interval;
switch (level) {
//each level increases drop speed by .10 seconds
case 1: interval = 800;
break;
case 2: interval = 700;
break;
case 3: interval = 600;
break;
case 4: interval = 500;
break;
default: interval = 1000;
break;
}
Timer t = new Timer(interval, new ActionListener() {
public void actionPerformed(ActionEvent e) {
//newPiece.autoMove();
//repaint();
}
});
t.start();
}
public void messageTimer() {
Timer t = new Timer(5000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
gameLost = false;
}
});
t.start();
}
//move piece on key input
public void keyPressed(KeyEvent e) {
newPiece.movePieceCheck(e.getKeyCode());
repaint();
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public boolean isValidCoordinate(int x, int y) {
return x >= 0 && y >= 0 && x < totalCols && y < totalRows;
}
// returns the tile at (x, y) or null if empty
public Tile getTileAt(int x, int y) {
if(isValidCoordinate(x, y))
return grid[x][y];
return null;
}
// sets the tile at (x, y) to tile
public void setTileAt(Tile tile, int x, int y) {
if(isValidCoordinate(x, y))
grid[x][y] = tile;
}
public boolean isOpen(int x, int y) {
return isValidCoordinate(x, y) && (getTileAt(x, y) == null);
}
public void collisionCheck() {
if (newPiece.isCollision()){
newPiece = new PieceSscce(this, randomPiece(), getColor());
}
}
public void changeLevel () {
int max = (level+1)*changeLevelMultiplier;
if (totalScore >= max) {
System.out.println(max + "reached... next level");
level++;
totalScore = 0;
timer();
}
}
public int tallyScore(int totalLines) {
int score = 0;
switch (totalLines) {
case 1: score = 40 * (level + 1);
break;
case 2: score = 100 * (level + 1);
break;
case 3: score = 300 * (level + 1);
break;
case 4: score = 1200 * (level + 1);
break;
default: break;
}
return score;
}
//loop through all rows starting at bottom (12 rows)
public void checkBottomFull() {
int lines = 0;
for(int row = 12; row > 0; row--) {
/* while (isFull(row)) {
lines++;
// clearRow(row);
}*/
}
totalScore += tallyScore(lines);
//check if level needs to be changed based on current score...
changeLevel();
//reset lines after score has been incremented
lines=0;
}
//loop through all columns in that row (10 columns)
public boolean isFull(int row) {
for (int col = 0; col <= 10; col++) {
System.out.println(row + ", " + col);
if(grid[row][col] == null) {
return false;
}
}
return true;
}
public void clearRow(int rowToClear) {
for(int row = rowToClear; row > 0; row--) {
for(int col = 0; col < grid[row].length; col++) {
grid[col][row] = grid[col][row-1];
}
}
}
public void checkEndGame(int x, int y) {
//if currPiece y location = 0 AND the space below is filled...
if (y <= 2 && !isOpen(x, y+1)) {
gameLost = true;
level = 0;
totalScore = 0;
//reset timer
timer();
for(int row = 0; row < grid.length; row++) {
for(int col = 0; col < grid[row].length; col++) {
grid[row][col] = null;
}
}
}
}
}
import java.awt.Color;
import java.awt.event.KeyEvent;
public class PieceSscce {
public int[] pieceCoordinates;
public String shape, currRotation;
public Color color;
public BoardSscce board;
public int rotationsCounter;
public TileSscce tile[];
public int[] newPositionX, newPositionY, currPositionX, currPositionY;
//don't need to pass in board because I'm already utilizing the Tiles class, which knows about the board
public Piece(Board b, String randomPiece, Color randomColor) {
shape = randomPiece;
color = randomColor;
board = b;
newPositionX = new int[4];
newPositionY = new int[4];
currPositionX = new int[4];
currPositionY = new int[4];
pieceCoordinates = new int[8];
//set pieceCoordinates global variable
getShape(shape);
tile = new TileSscce[4];
int counterX = 0, counterY = 1;
System.out.print("\"" + shape + "\" Coordinates: ");
//generate 4 new Tiles at specified coordinates that will compose the Piece
for (int i = 0; i < tile.length; i++) {
tile[i] = new TileSscce(board, pieceCoordinates[counterX], pieceCoordinates[counterY]);
System.out.print("(" + pieceCoordinates[counterX] + ", " + pieceCoordinates[counterY] + ") ");
//increment by 2 because x,y values are next to each other in array
counterX+=2;
counterY+=2;
}
System.out.println("\n");
for (int i = 0; i < tile.length; i++) {
tile[i].setColor(color);
}
}
public void calcNewPosition(int newX, int newY, int currTile) {
newPositionX[currTile] = newX;
newPositionY[currTile] = newY;
}
public void clearCurrPosition() {
for (int i = 0; i < tile.length; i++) {
currPositionX[i] = tile[i].getX();
currPositionY[i] = tile[i].getY();
board.setTileAt(null, currPositionX[i], currPositionY[i]);
}
}
public void autoMove() {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getX(), tile[i].getY()+1, i);
}
clearCurrPosition();
for (int i = 0; i < tile.length; i++) {
board.checkEndGame(tile[i].getX(), tile[i].getY());
System.out.println("Checking..." + tile[i].getX() + ", " + tile[i].getY());
}
board.checkBottomFull();
board.collisionCheck();
move();
}
public void movePieceCheck(int keycode) {
if (keycode == KeyEvent.VK_DOWN) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getX(), tile[i].getY()+1, i);
}
clearCurrPosition();
for (int i = 0; i < tile.length; i++) {
board.checkEndGame(tile[i].getX(), tile[i].getY());
System.out.println("Checking..." + tile[i].getX() + ", " + tile[i].getY());
}
board.checkBottomFull();
board.collisionCheck();
move();
}
if (keycode == KeyEvent.VK_RIGHT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getX()+1, tile[i].getY(), i);
}
clearCurrPosition();
move();
}
if (keycode == KeyEvent.VK_LEFT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getX()-1, tile[i].getY(), i);
}
clearCurrPosition();
move();
}
//rotate left
if (keycode == KeyEvent.VK_A) {
int[] rotatedCoords = calcRotation("left");
clearCurrPosition();
rotate(rotatedCoords, "left");
}
//rotate right
if (keycode == KeyEvent.VK_D) {
int[] rotatedCoords = calcRotation("right");
clearCurrPosition();
rotate(rotatedCoords, "right");
}
}
public boolean movePieceValid() {
boolean valid = true;
for (int i = 0; i < tile.length; i++) {
if(!tile[i].checkNewLocation(newPositionX[i], newPositionY[i]))
valid = false;
}
return valid;
}
public boolean validRotation(int[] rotatedCoordinates) {
boolean valid = true;
int counterX = 0, counterY = 1;
for (int i = 0; i < tile.length; i++) {
if(!tile[i].checkNewLocation(rotatedCoordinates[counterX], rotatedCoordinates[counterY]))
valid = false;
counterX +=2;
counterY +=2;
}
return valid;
}
public void move() {
if (movePieceValid()) {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(newPositionX[i], newPositionY[i]);
}
} else {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(currPositionX[i], currPositionY[i]);
}
}
}
public void rotate(int[] rotatedCoordinates, String rotation) {
int counterX = 0, counterY = 1;
if (validRotation(rotatedCoordinates)) {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(rotatedCoordinates[counterX], rotatedCoordinates[counterY]);
counterX+=2;
counterY+=2;
}
//else, if not valid move set the original location
} else {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(currPositionX[i], currPositionY[i]);
}
}
}
public boolean isCollision() {
boolean collision = false;
for (int i = 0; i < tile.length; i++) {
if(tile[i].collision(newPositionX[i], newPositionY[i])) {
collision = true;
}
}
return collision;
}
//calc curr coordinates, send them to getRotation... which will create new piece based on coords
public int[] calcRotation(String direction) {
for (int i = 0; i < tile.length; i++) {
currPositionX[i] = tile[i].getX();
currPositionY[i] = tile[i].getY();
System.out.println("Current position: (" + currPositionX[i] + "," + currPositionY[i]+")");
}
return getRotation(currPositionX, currPositionY, direction);
}
public int[] getRotation (int coordinatesX[], int coordinatesY[], String direction) {
int[] rotationDirection;
int[] coordinates = new int[8];
int[] origin = new int[2];
int[] newCoordinates = new int[8];
int[] resultCoordinates = new int[8];
int[] finalCoordinates = new int[8];
int vectorMatrix[][] = new int[2][4];
//set either R(90) or R(-90) rotation matrix values:
if (direction.equals("right")) {
rotationDirection = new int[] {0, -1, 1, 0};
}
else {
rotationDirection = new int[] {0, 1, -1, 0};
}
int counterX = 0, counterY = 1, x = 0;
while (counterY < coordinates.length) {
//add arrays coordinatesX and coordinatesY into a single array: coordinates
coordinates[counterX] = coordinatesX[x];
coordinates[counterY] = coordinatesY[x];
counterX+=2;
counterY+=2;
x++;
}
//set origin so it rotates around center...
if (shape.equals("RevZ")) {
origin[0] = coordinates[6];
origin[1] = coordinates[7];
}
else if (shape.equals("T")) {
origin[0] = coordinates[4];
origin[1] = coordinates[5];
}
else {
origin[0] = coordinates[2];
origin[1] = coordinates[3];
}
//subtract origin from vectors
System.out.println();
counterX = 0;
counterY = 1;
while (counterY < newCoordinates.length) {
//System.out.println(coordinates[counterX] + ", " + coordinates[counterY]);
newCoordinates[counterX] = coordinates[counterX] - origin[0];
newCoordinates[counterY] = coordinates[counterY] - origin[1];
System.out.println("Translated coordinates: (" + newCoordinates[counterX] + ", " + newCoordinates[counterY] + ")");
counterX+=2;
counterY+=2;
}
System.out.println();
System.out.println("vector matrix:");
//fill up vectorMatrix with coordinates
int k = 0;
for (int col = 0; col < 4; col++) {
for (int row = 0; row < 2; row++) {
vectorMatrix[row][col] = newCoordinates[k++];
}
}
//print vectorMatrix:
for (int i = 0; i < vectorMatrix.length; i++) {
System.out.print("[");
for (int j = 0; j < vectorMatrix[i].length; j++) {
System.out.print(vectorMatrix[i][j]);
}
System.out.println("]");
}
int rotationMatrix[][] = new int[2][2];
//fill up rotationMatrix
System.out.println();
System.out.println("multiplicative matrix:");
k = 0;
for (int row = 0; row < 2; row++) {
System.out.print("[");
for (int col = 0; col < 2; col++) {
rotationMatrix[row][col] = rotationDirection[k++];
System.out.print(rotationMatrix[row][col]);
}
System.out.println("]");
}
//perform matrix multiplication
int[][] result = multiplyMatrices(rotationMatrix, vectorMatrix);
//print resulting matrix
System.out.println();
System.out.println("result matrix:");
for (int i = 0; i < result.length; i++) {
System.out.print("[");
for (int j = 0; j < result[i].length; j++) {
System.out.print(result[i][j]);
}
System.out.println("]");
}
//load new matrix coordinates back into array
k = 0;
for (int col = 0; col < 4; col++) {
for (int row = 0; row < 2; row++) {
resultCoordinates[k] = result[row][col];
k++;
}
}
System.out.println();
System.out.println("result coordinates:");
counterX = 0;
counterY = 1;
while (counterY < resultCoordinates.length) {
finalCoordinates[counterX] = resultCoordinates[counterX] + origin[0];
finalCoordinates[counterY] = resultCoordinates[counterY] + origin[1];
System.out.print("("+finalCoordinates[counterX] + ", " + finalCoordinates[counterY]+")");
counterX+=2;
counterY+=2;
}
return finalCoordinates;
}
public int[][] multiplyMatrices(int rotationMatrix[][], int vectorMatrix[][]) {
int mA = rotationMatrix.length;
int nA = rotationMatrix[0].length;
int mB = vectorMatrix.length;
int nB = vectorMatrix[0].length;
if (nA != mB) throw new RuntimeException("Illegal matrix dimensions.");
int[][] C = new int[mA][nB];
for (int i = 0; i < mA; i++) {
for (int j = 0; j < nB; j++) {
for (int k = 0; k < nA; k++) {
C[i][j] += (rotationMatrix[i][k] * vectorMatrix[k][j]);
}
}
}
return C;
}
public int[] getShape(String shape) {
if (shape.equals("L")) {
//pieceCoordinates = new int[] {0, 1, 0, 2, 1, 2, 2, 2};
pieceCoordinates = new int[] {4, 0, 4, 1, 5, 1, 6, 1};
}
else if (shape.equals("O")) {
pieceCoordinates = new int[] {0, 1, 1, 1, 0, 2, 1, 2};
}
else if (shape.equals("Z")) {
pieceCoordinates = new int[] {0, 1, 1, 1, 1, 2, 2, 2};
}
else if (shape.equals("RevZ")) {
pieceCoordinates = new int[] {1, 1, 2, 1, 0, 2, 1, 2};
}
else if (shape.equals("Bar")) {
//pieceCoordinates = new int[] {0, 1, 1, 1, 2, 1, 3, 1};
pieceCoordinates = new int[] {0, 1, 1, 1, 2, 1, 3, 1};
}
else if (shape.equals("T")) {
pieceCoordinates = new int[] {1, 1, 0, 2, 1, 2, 2, 2};
}
else if (shape.equals("RevL")) {
pieceCoordinates = new int[] {0, 2, 1, 2, 2, 2, 2, 1};
}
return pieceCoordinates;
}
}
import java.awt.Color;
import java.util.Random;
public class TileSscce {
private BoardSscce board;
private int currX, currY;
private Color color;
public TileSscce(BoardSscce b, int x, int y) {
board = b;
//when Tile is instantiated, set its position
setLocation(x, y);
}
public int getX() {
return currX;
}
public int getY() {
return currY;
}
public boolean checkNewLocation(int newX, int newY) {
boolean newLocationOK = board.isOpen(newX, newY);
return newLocationOK;
}
public boolean collision(int newX, int newY) {
boolean collision = this.getY() == ((board.getHeight()/board.getTileSize()))-2 || board.getTileAt(newX, newY) != null;
return collision;
}
public void setLocation(int newX, int newY) {
// board.setTileAt(null, currX, currY);
currX = newX;
currY = newY;
board.setTileAt(this, currX, currY);
}
public Color getColor() {
return setColor(color);
}
public Color setColor(Color myColor) {
color = myColor;
return color;
}
}
Thanks!
EDIT----------
I've tried implementing both ValarDohaeris and Svend Hansen's suggestions... Now the block is moving right when I press down, up when I press left, and down when I press right...
It seems to have to do with these methods in Board class which get and set tile locations...
// returns the tile at (x, y) or null if empty
public Tile getTileAt(int row, int col) {
System.out.println("getTileAt: " + row + ", " + col);
if(isValidCoordinate(row, col))
return grid[row][col];
return null;
}
// sets the tile at (x, y) to tile
public void setTileAt(Tile tile, int row, int col) {
System.out.println("setTileAt: " + row + ", " + col);
if(isValidCoordinate(row, col))
grid[row][col] = tile;
}
And in Piece class... movements are defined as:
public void movePieceCheck(int keycode) {
if (keycode == KeyEvent.VK_DOWN) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow()+1, tile[i].getCol(), i);
}
clearCurrPosition();
for (int i = 0; i < tile.length; i++) {
board.checkEndGame(tile[i].getRow(), tile[i].getCol());
}
board.checkBottomFull();
if (isCollision()) board.createNewPiece();
move();
}
if (keycode == KeyEvent.VK_RIGHT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow(), tile[i].getCol()+1, i);
}
clearCurrPosition();
move();
}
if (keycode == KeyEvent.VK_LEFT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow(), tile[i].getCol()-1, i);
}
clearCurrPosition();
move();
}
You have
grid = new TileSscce[totalRows][totalCols];
So when you want to access grid[x][y], you should check
x >= 0 && y >= 0 && x < totalRows && y < totalCols
in isValidCoordinate(x, y).
Emm... Quite interesting question. So to find out where the problem(s) may be I'll try to analyze your code a little bit...
You paste stack trace as
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 10
at Board.getTileAt(Board.java:177)
...
and at the same time the getTileAt()
// returns the tile at (x, y) or null if empty
public Tile getTileAt(int row, int col) {
System.out.println("getTileAt: " + row + ", " + col);
if(isValidCoordinate(row, col))//isValidCoordinate()?
return grid[row][col];
return null;
}
public boolean isValidCoordinate(int x, int y) {
return x >= 0 && y >= 0 && x < totalCols && y < totalRows;
}
... so the isValidCoordinate method return terms as
x >= 0 && y >= 0 && x < totalCols && y < totalRows
...the method doesn't allow to avoid array out-of-bounds problems; Seems like you put wrong array element indexes.
A. As I can notice, you trying to put a classic math matrix on Java [][] arrays as
public void clearRow(int rowToClear) {
for(int row = rowToClear; row > 0; row--) {
for(int col = 0; col < grid[row].length; col++) {//<-- ?
grid[col][row] = grid[col][row-1];
}
}
}
... and here I must say that you should know that in [][] arrays x,y are backwards and it is y,x because :
y (or classic i) - sub-array index (vertical)
x (or classic j) - sub-array's element index (horizontal)
so you should use array index something this way grid[y][x] or grid[i][j]
As a useful tip, I recommend you to analyze your code for logic errors in this field...
B. According to your app screenshot as
... it seems like the x,y problem takes place here too because you trying to control y (vertical) coordinates but (in real) you control x (horizontal) coordinates only :S It is still because of the row,col instead of a classic Java (col,row or y,x) [][] array index positions.
C. And again concerning to the wrong directions...
...up when I press left, and down when I press right...
if (keycode == KeyEvent.VK_RIGHT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow(), tile[i].getCol()+1, i);
}
clearCurrPosition();
move();
}
Here I'll try to analyze the event as (you press right but move down)...
OK... according to one of your tasks you need to move by x coordinate (horizontally) but look closer... you make tile[i].getCol()+1 so it is newY and, of course, it moves vertically :S In your case it really moves down because you make increment as y++ ...
public void calcNewPosition(int newX, int newY, int currTile) {
newPositionX[currTile] = newX;
newPositionY[currTile] = newY;
}
public void clearCurrPosition() {
for (int i = 0; i < tile.length; i++) {
currPositionX[i] = tile[i].getX();
currPositionY[i] = tile[i].getY();
board.setTileAt(null, currPositionX[i], currPositionY[i]);
}
}
public void move() {
if (movePieceValid()) {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(newPositionX[i], newPositionY[i]);//<-- !
}
} else {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(currPositionX[i], currPositionY[i]);
}
}
}
...as a conclusion, I may recommend to change code (move right) something this way...
if (keycode == KeyEvent.VK_RIGHT) {
for (int i = 0; i < tile.length; i++) {
calcNewPosition(tile[i].getRow()+1, tile[i].getCol(), i);
}
clearCurrPosition();
move();
}
I hope my tips will help you to figure out what to look closer. Anyway, if you have some additional information please do comment my answer
Report if that helped you
This is based on x corresponds to columns and y corresponds to rows.
However grid is indexed by [row][col].
TileSscce grid[][] = new TileSscce[totalRows][totalCols]; // 12 => totalRows, 10 => totalCols
public int getWidth() {
return totalCols * tilesize;
}
public int getHeight() {
return totalRows * tilesize;
}
Following changes (based on your initial code - Sscce: - without later edits) will get rid of the exception and allow drawing till bottom of the board.
public void paintComponent(Graphics g) {
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
if (grid[row][col] != null) {
g.setColor(grid[row][col].getColor());
g.fillRect(col * tilesize, row * tilesize, tilesize, tilesize); // changed, check below snippet from fillRect documentation
g.setColor(Color.WHITE);
}
}
}
}
public TileSscce getTileAt(int x, int y) {
if (isValidCoordinate(x, y))
return grid[y][x]; // changed to [y][x] as grid is indexed by [row][col]
return null;
}
public void setTileAt(TileSscce tile, int x, int y) {
if (isValidCoordinate(x, y))
grid[y][x] = tile; // changed to [y][x] as grid is indexed by [row][col]
}
From fillRect documentation.
public abstract void fillRect(int x, int y, int width, int height)
The left and right edges of the rectangle are at x and x + width - 1.
The top and bottom edges are at y and y + height - 1.
This is correct.
public boolean isValidCoordinate(int x, int y) {
return x >= 0 && y >= 0 && x < totalCols && y < totalRows;
}
I made a simple FlowChat Editor that creates rectangles and triangles and connects them to each other and shows the way from up to down. I can move this elements on screen too.
I am now trying to create a button to delete the element which I clicked. There is problem that I can delete MyTriangle objects, but I can't delete MyRectangle objects. It deletes but not object which I clicked. I delete from first object to last.
Here is my code:
if (deleteObj) {
if (rectsList.size() != 0) {
for (int i = 0; i < rectsList.size(); i++) {
MyRect rect = (MyRect) rectsList.get(i);
if (e.getX() <= rect.c.x + 50 && e.getX() >= rect.c.x - 50
&& e.getY() <= rect.c.y + 15 && e.getY() >= rect.c.y - 15) {
rectsList.remove(rect);
System.out.println("This is REctangle DELETED\n");
}
}
}
if (triangleList.size() != 0) {
for (int j = 0; j < triangleList.size(); j++) {
MyTriangle trian = (MyTriangle) triangleList.get(j);
if (e.getX() <= trian.c.x + 20 && e.getX() >= trian.c.x - 20
&& e.getY() <= trian.c.y + 20 && e.getY() >= trian.c.y - 20) {
triangleList.remove(trian);
System.out.println("This is Triangle Deleted\n");
}
}
}
Edit Here MyRectangle and MyTriangle classes
public class MyRect extends Ellipse2D.Double {
Point c;
Point in;
Point out;
int posX;
int posY;
int width = 100;
int height = 30;
int count;
public MyRect(Point center, Point input, Point output,int counter) {
c = center;
in = input;
out = output;
count=counter;
}
void drawMe(Graphics g) {
// in.x=c.x+20;
int posX = c.x;
int posY = c.y;
int posInX = in.x;
int posInY = in.y;
int posOutX = out.x;
int posOutY = out.y;
g.setColor(Color.MAGENTA);
g.drawString(" S "+count ,posX-5, posY+5);
g.setColor(Color.black);
g.drawRect(posX-50, posY-15, width, height);
g.setColor(Color.green);
g.drawRect(posInX-3, posInY-9, 6, 6);
g.setColor(Color.blue);
g.drawRect(posOutX-3, posOutY+3, 6, 6);
}
}
public class MyTriangle {
Point c;
Point in ;
Point outYES ;
Point outNO ;
int posX;
int posY;
int count;
public MyTriangle(Point center,Point input,Point outputYES,Point outputNO,int counter) {
c = center;
in = input;
outYES = outputYES;
outNO = outputNO;
count=counter;
}
void drawMe(Graphics g) {
int posX = c.x;
int posY = c.y;
int posInX=in.x;
int posInY=in.y;
int posOutYESX=outYES.x;
int posOutYESY=outYES.y;
int posOutNOX=outNO.x;
int posOutNOY=outNO.y;
int[] xPoints = {posX - 50, posX, posX + 50, posX};
int[] yPoints = {posY, posY - 30, posY, posY + 30};
g.setColor(Color.MAGENTA);
g.drawString(" T "+count,posX-5, posY+5);
g.setColor(Color.black);
g.drawPolygon(xPoints, yPoints, 4);
// draw input
g.setColor(Color.green);
g.drawRect(posInX-3,posInY-9, 6, 6);
g.setColor(Color.blue);
g.drawRect(posOutYESX-9,posOutYESY-3 , 6, 6);
g.setColor(Color.red);
g.drawRect(posOutNOX-3,posOutNOY+3 , 6, 6);
}
}
Edit 2
Here my funcs to add object to list.Is there could be ant mistake?Because I am her ceratıng new object of that calss and I am addıng that object to rectlist or trianglelist..
public void initRect(Point c, Point in, Point out) {
sCounter++;
MyRect myrects = new MyRect(c, in, out, sCounter);
rectsList.add(myrects);
s_And_t_List.add(myrects);
objectCounter.add("S " + sCounter);
selectablePanel.repaint();
}
public void initTriangle(Point c, Point in, Point outYES, Point outNO) {
tCounter++;
MyTriangle mytriangles = new MyTriangle(c, in, outYES, outNO, tCounter);
triangleList.add(mytriangles);
s_And_t_List.add(mytriangles);
objectCounter.add("T " + tCounter);
selectablePanel.repaint();
}
It seems like your logic is wrong. In your Rectangle class why not make a method returning a boolean that tests if a given set of coordinates is contained in your object. For example:
public boolean contains(int x, int y){
if(x_starting_point <= x && x <= x_starting_point+width
&& y_starting_point <= y && y <= y_starting_point+height)
return true;
return false;
}