Java Tetris rotation - java

I know this has been asked a lot but I'd like to know how to rotate a Tetris piece?
I already made a long and bad solution (~170 lines of code) but there should be easier way to do it.
My tetris pieces consist of 4 blocks which all know their location (row and column) in the matrix. Matrix itself is char-typed, so 4 blocks are all letters. It looks like this for example:
......
..T...
.TTT..
......
I tried to simulate my matrix as coordinate system by counting the middle row and column and using it as an origo and then tried to apply this simple algorithm I found:
90 degree rotation (x,y) = (-y,x)
It appears that it works only if my piece is in the center of matrix. I have no idea what I should do, I've been thinking this all day. Here's my method:
public void rotatePiece(ArrayList<Block> random) {
int distance = 0; // how far is the origo
for (int i=0; i < 4; ++i)
board[random.get(i).getRow()][random.get(i).getColumn()] = '.'; // erases the current location of the piece
for (int i=0; i < 4; ++i) {
distance = Math.abs(random.get(i).getColumn()-middleColumn);
if (random.get(i).getColumn() < middleColumn)
random.get(i).setColumn(random.get(i).getColumn()+(distance*2)); // "throws" the location of the block to the other side of the origo
else
random.get(i).setColumn(random.get(i).getColumn()-(distance*2));
int help = random.get(i).getColumn();
random.get(i).setColumn(random.get(i).getRow()); // (x, y) = (-y, x)
random.get(i).setRow(help);
}
for (int i=0; i < 4; ++i)
board[random.get(i).getRow()][random.get(i).getColumn()] = random.get(0).getStyle(); // saves the new location of the piece in the matrix

I would recommend defining four states for each block-group.
enum ROTATION {
UP, DOWN, LEFT, RIGHT;
ROTATION rotateLeft() {
switch(this) {
case UP: return LEFT;
case LEFT: return DOWN;
case DOWN: return RIGHT;
case RIGHT: return UP;
}
return null; // wont happen;
}
ROTATION rotateRight() {
ROTATION r = this;
// wow I'm lazy, but I've actually seen this in production code!
return r.rotateLeft().rotateLeft().rotateLeft();
}
}
abstract class Brick {
Point centerPos;
ROTATION rot;
abstract List<Point> pointsOccupied();
}
class TBrick extends Brick {
List<Point> pointsOccupied() {
int x = centerPos.x();
int y = centerPos.y();
List<Point> points = new LinkedList<Point>();
switch(rot) {
case UP: points.add(new Point(x-1,y);
points.add(new Point(x,y);
points.add(new Point(x+1,y);
points.add(new Point(x, y+1);
break;
case Down: points.add(new Point(x-1,y);
points.add(new Point(x,y);
points.add(new Point(x+1,y);
points.add(new Point(x, y-1);
break;
// finish the cases
}
}
}

You can use a rotation matrix.
You will need to set the origin of your rotation appropriately, which may mean translating the location of the piece with respect to the playing field (such that the origin is in the centre, for example), applying the rotation matrix and then translating it back to its correct location on the playing field coordinates.

The easiest and computational fastest way to do this, would to use precompute them.
That means a tetris piece will look like
class TetrisBlock {
String position[4];
int curPos = 0;
void rotateLeft() {
curPos++;
if (curPos > 3)
curPos = 0;
}
....
}
And then you could define something like
class TetrisTBlock extends TetrisBlock {
...
// in constructor
position={"....\n.T..\nTTT.\n....",
".T..\nTT..\n.T..\n.....",
// I guess you get the idea
...
You do this for every type of block and then you can also add members for adding/removing them from the board.
If you optimize you would go away from the chars....

I think the best way is to hard-code it. Take into consideration that each figure is different and each of the figure's rotation phase is also different. And for each rotation phase - determine which parts of the grid you need to be free (avoid collision).
For a visual representation check this

Related

tile disappearing from rubiks cube with rotation

I'm working on a 2x2 rubik cube, and was having trouble getting one side rotate with my program. The cube is a 2d array of squares. I'm just triying to do a 90 degree counter clockwise turn.
This is what happens
https://imgur.com/a/tlskNKY
I changed the colour so I could see the specific squares and how they changed. I tried changing the order, moving specific pieces at a time to see if the problem was just overlapping pieces (no such luck).
//square class
public class square implements Comparable {
int c;
private Rectangle r;
int xpos, ypos, width, height;
public square(int a, int x, int y) {
c = a;
xpos = x;
ypos = y;
r = new Rectangle(xpos, ypos, 50, 50);
}
//some unused methods
}
//inside the cube class
public class cube{
square[] temp = new square[4]
square[][] sq= new square[6][4]
//two for loops make squares and fills the sq 2d array
//the result is in the imgur link
public void turnVc(){
temp= sq[2];
sq[2][0]=temp[1];
sq[2][1]=temp[3];
sq[2][2]=temp[2];
sq[2][3]=temp[0];
}
}
I expect the output to be the original image turned counter clockwise.
tmp is a pointer that points to the same object that sq[2] pointers. That's why when you change sq[2] content, you change tmp's as well.
i think instead of assign "temp= sq[2];" you should do the following:
temp = new square[4];
for (int i = 0; i < 4; i++) {
temp[i] = sq[2][i];
}
Edit:
i think a little improvement you could do is that you don;t need to save all the sq[2] array, you could only save the fist item. i would do like this (tmp is now a square, not an array):
tmp = sq[2][0];
sq[2][0] = sq[2][1];
sq[2][1] = sq[2][3];
sq[2][3] = sq[2][2];
sq[2][2] = tmp;
If your square class implements Cloneable, you should use clone() method possible, it is also similar to answer of #Nguyen Tan Bao, but shorter
I guess you 're C++ dev, reference in Java is like pointer in C++, you can research more Have fun !

How can I get the different results as I put the number?

I wanna get the different results as the inputted number.
For example, when I put 4 I get the results of the rectangle and when I put 3 I get the triangle.
import java.util.Scanner;
public class Source9_1 {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int x, y; // 클래스 Parameter(내부 변수)
Point[] v = new Point[n]
for(int i=0; i <= v.length; i++) {
v[i] = new Point();
v[i].
}
}
class Point {
int x, y; // 클래스 Parameter (내부 변수)
public void setPoint(int x, int y) { // Point 세팅
this.x = x;
this.y = y;
}
public void printPoint() { // Point 출력
System.out.println("x = " + x + ", y = " + y);
}
}
class Rectangle extends Point {
Point[] p = new Point[4];
Rectangle(Point[] p) {
this.p = p;
}
}
class Triangle extends Point {
Point[] p = new Point[3]; // 3개의 Point인스턴스를 담을 배열 생성
Triangle(Point[] p) {
this.p = p;
}
}
class Shape extends Point { // Point 배열 및 상속을 받아 세팅 후 출력가능한 클래스
Point coord[10];
static int s = 0; // 불릴 때마다 값 증가 ???
public void printShapePoint() { // 배열에 담은 Point 출력
}
public void setShapePoint() { // 배열에 담기 위해 Point 증가
}
}
So far, I coded like this but I don't know what to do now.
How can I get the different result as I put the number?
This is the result what I want
First of all, about your Rectangle and Triangle classes. I feel like you have missed your point there (pun not intended), because you've put both extending the Point class. That doesn't make much sense, since you have the Shape class, which would do a much better job as the superclass for them.
So:
class Rectangle extends Shape {
...
}
class Triangle extends Shape {
...
}
With that out of the way, what you have so far:
You are capturing the number of points from the input;
You are creating an array of that size;
You are instantiating and setting an Point object for each of the array positions.
What you need to do next:
Capture the points coordinates from the input
Set said coordinates to the Point objects
Instantiate an Triangle or Rectangle object, depending on how many points you have.
So, inside your for statement you will want to do:
for (int i=0; i <= v.length; i++) {
v[i] = new Point();
x = sc.nextInt(); // Save 'x' value into the variable
y = sc.nextInt(); // Save 'y' value into the variable
v[i].setPoint(x, y); // Set both values using the method from Point
}
Then, since both Rectangle and Triangle have Shape as a common superclass, you are allowed to put objects of either one of these class in a Shape variable. So right after the for statement you will want to do:
Shape s; // Create the empty variable here, so it will exist outside the if-else scope
if (n == 3)
s = new Triangle(v);
else
s = new Rectangle(v);
Finally, just print your points:
for (int i = 0; i < v.length; i++)
v[i].printPoint();
And that's pretty much it.
The answer from Pedro is great. I have an additional suggestion to make. Using a switch or conditional to create the different types of shapes is a bit of a code smell. I suggest using an abstract factory for this. I put up a little example of how you could do this here.
Deducing the shape from the number of points might be insufficient. For instance, a rectangle is defined by two points (not four) and so would a line, even though you are not currently modeling lines.
I think it would be more clear to select the shape by name and use a factory to instantiate it from the entered points.
Note that he shapes object hierarchy is used a lot to explain object orientation. There are several pitfalls with designing class structures like this. See for example this article. Keep in mind also the Liskov Substitution principle which is easily violated, see this article.

Draw and get OpenSimplexNoise result

I want to generate a random terrain with OpenSimplexNoise. To start I just want to get a result and draw it to a window.
My question is now: How can I get the correct output of OpenSimplexNoise (cause there are many methods and I just don't know which is the correct one) and how to draw this result.
It should look like this:
public double[][] generateMap(long seed, int width, int height) {
double[][] map = new double[width][height];
// start generating things here, just how?
OpenSimplexNoise simplex = new OpenSimplexNoise(seed);
return map;
}
public void drawMap(double[][] map, Graphics g) {
for(int x = 0; x < map.length; x++) {
for(int y = 0; y < map[0].length; y++) {
Color color = new Color(); // how to get the color here?
}
}
}
This is the current code I've got.
Here is the link to OpenSimplexNoise for anyone who needs it:
https://gist.github.com/KdotJPG/b1270127455a94ac5d19
There's actually only 3 public methods - one for each for 2D, 3D & 4D noise.
Since you're filling a 2D array for your map , use the 2D noise eval method,
something like this:
for(int x=0; x<width; x++){
for(int y=0<y<height; y++){
map[x][y] = simplex.eval(x, y);
}
}
Later on, you can generate a color from the map values as follows:
Color color = Color.color(map[x][y], ma[x][y], map[x][y]);
The author also provides example usage code in OpenSimplexNoiseTest; he's using the 3D eval method, but always holding the z coord at zero. My guess is the example code was written before he added the 2D & 4D implementations. At any rate, it still works, but it might be a little slower than directly using 2D.

plotting points along a straight line from a random start position

I am looking for some help with some game code i have inherited from a flight sim. The code below simulates bombs exploding on the ground, it works fine but i am trying to refine it.
At the moment it takes a random value for x and y as a start point and then adds another random value between -20 and 20 to this. It works ok, but doesn't simulate bombs dropping very well as the pattern does not lay along a straight line/
What i would like to achieve though is all x and y points after the first random values, to lay along a straight line, so that the effects called for all appear to lay in a line. It doesn't matter which way the line is orientated.
Thanks for any help
slipper
public static class BombUnit extends CandCGeneric
{
public boolean danger()
{
Point3d point3d = new Point3d();
pos.getAbs(point3d);
Vector3d vector3d = new Vector3d();
Random random = new Random();
Aircraft aircraft = War.GetNearestEnemyAircraft(this, 10000F, 9);
if(counter > 10)
{
counter = 0;
startpoint.set(point3d.x + (double)(random.nextInt(1000) - 500), point3d.y + (double)(random.nextInt(1000) - 500), point3d.z);
}
if(aircraft != null && (aircraft instanceof TypeBomber) && aircraft.getArmy() != myArmy)
{
World.MaxVisualDistance = 50000F;
counter++;
String s = "weapon.bomb_std";
startpoint.x += random.nextInt(40) - 20;
startpoint.y += random.nextInt(40) - 20;
Explosions.generate(this, startpoint, 7F, 0, 30F, !Mission.isNet());
startpoint.z = World.land().HQ(startpoint.x, startpoint.y);
MsgExplosion.send(this, s, startpoint, getOwner(), 0.0F, 7F, 0, 30F);
Engine.land();
int i = Landscape.getPixelMapT(Engine.land().WORLD2PIXX(startpoint.x), Engine.land().WORLD2PIXY(startpoint.y));
if(firecounter < 100 && i >= 16 && i < 20)
{
Eff3DActor.New(null, null, new Loc(startpoint.x, startpoint.y, startpoint.z + 5D, 0.0F, 90F, 0.0F), 1.0F, "Effects/Smokes/CityFire3.eff", 300F);
firecounter++;
}
super.setTimer(15);
}
return true;
}
private static Point3d startpoint = new Point3d();
private int counter;
private int firecounter;
public BombUnit()
{
counter = 11;
firecounter = 0;
Timer1 = Timer2 = 0.05F;
}
}
The code in the question is a mess, but ignoring this and trying to focus on the relevant parts: You can generate a random position for the first point, and a random direction, and then walk along this direction in several steps.
(This still raises the question of whether the direction is really not important. Wouldn't it matter if only the first bomb was dropped in the "valid" area, and the remaining ones outside of the screen?)
However, the relevant code could roughly look like this:
class Bombs
{
private final Random random = new Random(0);
int getScreenSizeX() { ... }
int getScreenSizeY() { ... }
// Method to drop a single bomb at the given position
void dropBombAt(double x, double y) { ... }
void dropBombs(int numberOfBombs, double distanceBetweenBombs)
{
// Create a random position in the screen
double currentX = random.nextDouble() * getScreenSizeX();
double currentY = random.nextDouble() * getScreenSizeY();
// Create a random step size
double directionX = random.nextDouble();
double directionY = random.nextDouble();
double invLength = 1.0 / Math.hypot(directionX, directionY);
double stepX = directionX * invLength * distanceBetweenBombs;
double stepY = directionY * invLength * distanceBetweenBombs;
// Drop the bombs
for (int i=0; i<numberOfBombs; i++)
{
dropBombAt(currentX, currentY);
currentX += stepX;
currentY += stepY;
}
}
}
I am assuming your startpoint is a StartPoint class with x,y,z coordinates as integers in it.
I hope I have understood your problem correctly. It looks like you either want to create a vertical explosion or a horizontal explosion. Since an explosion always occurs on ground, the z coordinate will be zero. Now you can vary one of x or y coordinate to give you a random explosion along a straight line. Whether you choose x or y could be fixed or could be randomized itself. A potential randomized solution below:
public boolean danger() {
// stuff
int orientation = Random.nextInt(2);
if(aircraft != null && (aircraft instanceof TypeBomber) && aircraft.getArmy() != myArmy)
{
// stuff
startPoint = randomizeStartPoint(orientation, startPoint);
// stuff
}
}
StartPoint randomizeStartPoint(int orientation, StartPoint startPoint) {
if(orientation == 0) {
startPoint.x += random.nextInt(40) - 20;
}
else {
startPoint.y += random.nextInt(40) - 20;
}
return startPoint;
}
In response to the image you uploaded, it seems that the orientation of the explosion need not necessarily be horizontal or vertical. So the code I posted above gives a limited solution to your problem.
Since you want any random straight line, your problem boils down to two sub parts:
1. Generate a random straight line equation.
2. Generate random point along this line.
Now, a straight line equation in coordinate geometry is y = mx + c where m is the slope and c is the constant where the line crosses the y-axis. The problem with c is that it gives rise to irrational coordinates. I am assuming you are looking for integer coordinates only, since this will ensure that your points are accurately plotted. (You could do with rational fractions, but then a fraction like 1/3 will still result in loss of accuracy). The best way to get rid of this irrational problem is to get rid of c. So now your straight line always looks like y = mx. So for step one, you have to generate a random m.
Then for step 2, you can either generate a random x or random y. It doesn't matter which one, since either one will result in random coordinates.
Here is a possible code for the solution:
int generateRandomSlope() {
return Random.nextInt(100); // arbitrarily chose 100.
}
int randomizeStartPoint(int m, StartPoint startPoint) { // takes the slope we generated earlier. without the slope, your points will never be on a straight line!
startPoint.x += random.nextInt(40) - 20;
startPoint.y += x * m; // because a line equation is y = mx
return startPoint;
}
public boolean danger() {
// stuff
int m = generateRandomSlope(); // you may want to generate this elsewhere so that it doesn't change each time danger() is called.
if(aircraft != null && (aircraft instanceof TypeBomber) && aircraft.getArmy() != myArmy)
{
// stuff
startPoint = randomizeStartPoint(m, startPoint);
// stuff
}
}
Again, this is not a complete or the best solution.

Android development - objects moving for no reason

I'm trying to make a pinball-style game for a school project for the Android in the SDK in Eclipse.
There's a really weird and super frustrating problem in which objects are moving without any code telling them to. Basically, each Wall instance contains 4 Line objects which are used for collision detection with the Ball. These Lines work the first time, but as soon as the ball collides with them once, then that Line somehow moves to another position on the screen.
I've been debugging it, and wouldn't ask if I hadn't already tried everything, but there is honestly no reason for the Line to shift anywhere. The way I handle a collision is by pushing the Ball to be 1px away from the wall, and then given new dx and dy (velocities) to move away. The code for checking for collisions is below, followed by the function that handles a collision to change the ball's position and velocity. Both are methods in the Ball class.
GameElement[] walls = currLevel.getWalls();
int i, j;
Line[] lines;
Line line;
RectF lineBounds;
boolean hadCollision = false;
for (i = 0; i < walls.length & !hadCollision; i++) {
lines = walls[i].getLines();
for (j = 0; j < lines.length & !hadCollision; j++) {
lineBounds = lines[j].getBounds();
if (lineBounds.intersect(point)) {
paint.setColor(Color.BLUE); // Colour ball blue.
reactToCollision3(lines[j]);
// TEST RESET!!!
//this.x = (float)(648+40);
//this.y = (float)(900-30);
hadCollision = true;
//printWallsLines();
}
}
}
and the function to handle the collision is:
public void reactToCollision3 (Line line) {
float liney = line.sy;
float linex = line.sx;
if (line.rotation == 0.0) { // HORIZONTAL EDGE
if (this.y > liney) { // Ball moving upward hits the bottom of a wall.
this.y = liney + this.radius + 1.0f;
} else { // Ball moving downward hits the top of a wall.
this.y = liney - this.radius - 1.0f;
}
this.dy *= -1.0f;
} else { // VERTICAL EDGE
if (this.x > linex) { // Ball moving leftward hits right edge of a wall.
this.x = linex + this.radius + 1.0f;
} else { // Ball moving rightward hits left edge of a wall.
this.x = linex - this.radius - 1.0f;
}
this.dx *= -1.0f;
}
So when I run this right now, the ball will bounce off a wall the first time it hits it, and then that line (edge of the wall) that it hit will be shifted elsewhere, but is not visible because the Wall is drawn as one unit so the Lines that comprise it don't affect the drawing.
If I comment out the lines for "this.x = ..." and "this.y = ...", then this problem doesn't happen anymore. Also, if I uncomment the test RESET lines for setting the ball's position in the above function, then the line doesn't shift then either. But as soon as I run this, it happens again.
I'm going insane looking for why this would happen. Please give me suggestions.
Thank you!
Did you intend to use bitwise &? (See ** in code) Your test for "!hadCollision" will fail and you will also be masking your wall length. I believe you meant to use &&
for (i = 0; i < walls.length **&** !hadCollision; i++) {
lines = walls[i].getLines();
for (j = 0; j < lines.length **&** !hadCollision; j++) {
lineBounds = lines[j].getBounds();
if (lineBounds.intersect(point)) {
paint.setColor(Color.BLUE); // Colour ball blue.
reactToCollision3(lines[j]);
// TEST RESET!!!
//this.x = (float)(648+40);
//this.y = (float)(900-30);
hadCollision = true;
//printWallsLines();
}
}
}

Categories