I'm making Tetris in Java and am working on rotating a piece.
To start, I was just rotating the bar-shaped piece.
I feel the way I'm doing it now is not only buggy, but complete overkill. But I'm not sure how else to go about it.
Firstly, I have a keylistener that sets int[] rotatedCoords equal to calcRotation("right")... If rotate Left, the rotationsCounter+=1; will be decremented instead.
if (keycode == KeyEvent.VK_D) {
int[] rotatedCoords = calcRotation("right");
rotationsCounter+=1;
clearCurrPosition();
rotate(rotatedCoords);
System.out.println(rotationsCounter);
if (rotationsCounter == 4) {
rotationsCounter = 0;
}
System.out.println(rotationsCounter);
}
calcRotation(String right or left) gets the current coordinates of all 4 Tiles in a Piece and sends them to int[] getRotation(String shape, String direction, int[] current X coords, int[] current Y 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(currPositionX[i] + ", " + currPositionY[i]);
}
return getRotation("Bar", direction, currPositionX, currPositionY);
}
then getRotation[] sets the new coordinates based on which rotation direction was chosen (right or left), which shape it is, and which rotation counter it's on (0 degrees, 90 degrees, 180 or 270...)
if (direction == "right") {
if (shape == "Bar") {
if (rotationsCounter == 0) {
currXs[0] += 1;
currYs[0] += -1;
currXs[1] += 0;
currYs[1] += 0;
currXs[2] += -1;
currYs[2] += 1;
currXs[3] += -2;
currYs[3] += 2;
rightRotate1 = new int[] {currXs[0], currYs[0], currXs[1], currYs[1], currXs[2], currYs[2], currXs[3], currYs[3]};
}
if (rotationsCounter == 1) {
... etc
Then the coordinates (pieceRotations) would be set appropriately:
//handle on left rotations
if (direction == "right") {
if (shape == "Bar") {
if (rotationsCounter == 0) {
pieceRotations = rightRotate1;
}
if (rotationsCounter == 1) {
pieceRotations = rightRotate2;
}
if (rotationsCounter == 2) {
pieceRotations = rightRotate3;
}
if (rotationsCounter == 3) {
pieceRotations = rightRotate0;
}
}
}
if (direction == "left") {
if (shape == "Bar") {
if (rotationsCounter == 0) {
pieceRotations = rightRotate3;
}
if (rotationsCounter == 1) {
pieceRotations = rightRotate0;
}
if (rotationsCounter == 2) {
pieceRotations = rightRotate1;
}
if (rotationsCounter == 3) {
pieceRotations = rightRotate2;
}
}
}
return pieceRotations;
}
Then finally, rotate(rotatedCoords) would be called with the correct coordinates to rotate all the tiles too...
public void rotate(int[] rotatedCoordinates) {
int counterX = 0, counterY = 1;
if (movePieceValid()) {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(rotatedCoordinates[counterX], rotatedCoordinates[counterY]);
counterX+=2;
counterY+=2;
}
} else {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(currPositionX[i], currPositionY[i]);
}
}
}
So, my current way of calculating the new coordinates based on current position of each shape for left or right is clearly overkill. Is there a general guide I can follow to greatly simplify that? I can't think of another way to get the positions for each shape?
There are only so many pieces. Try having this:
public abstract class Piece {
public abstract void rotate(Direction dir);
}
public class BarPiece extends Piece {
public void rotate(Direction dir) {
// implement
}
}
public class TPiece extends Piece {
// ...
}
public class LeftSPiece extends Piece {
// ...
}
It seems a bit dirty to special but doing math all the time will be slow, and since there are only so many possible pieces...
Related
So I have a solid slide function, the problem is (which is very hard to explain!) it goes through all the possibilities including spaces in the 2d array that have already been added together: say there is a setup like this: 4,4,8,2 ---after one swipe to the right, it ends up like this: ,_,16,2. however in the actual game, after one swipe right, it should look like this: ___,8,2.
Basically, how can I fix this? You don't need to tell me the code, it's my project for my final, but I'd like to get some sort of explanation as to why this is happening.
I've attempted to loop through the array from right to left but that resulted in the numbers not even moving.
processUserChoice(String i) {
if (i.equals("d")) {
while ((slideRight())) {
moveRight();
}
}
printBoard();
}
public boolean slideRight() {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length - 1; j++) {
if (board[i][j + 1] == 0 && board[i][j] != 0) {
return true;
} else if (board[i][j + 1] == board[i][j] && board[i][j] != 0) {
return true;
}
}
}
return false;
}
public void moveRight() {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length - 1; j++) {
if (board[i][j + 1] == 0 && board[i][j] != 0) {
board[i][j + 1] = board[i][j];
board[i][j] = 0;
} else if (board[i][j + 1] == board[i][j] && board[i][j] != 0) {
board[i][j + 1] = board[i][j + 1] + board[i][j];
board[i][j] = 0;
}
}
}
//checkLose();
}
After one swipe right, it should look like this:" ___,8,2 "(From the example before).
I did something similar to this in the past. I would to do it pretty much the same way as you are at the moment but add a Boolean array that checks if the tile has collided or not and only merges tiles that haven't been collided.
class Tile {
public int value;
public Boolean collided;
public Tile(int value) {
this.value = value;
collided = false;
}
public Tile attemptMerge(Tile target) {
if (target.value == value) {
Tile t = new Tile(value * 2);
t.collided = true;
return t;
} else {
return null;
}
}
public void reset() {
value = 0;
collided = false;
}
}
Somewhere in your main update loop:
void slideRight() {
for (int row = 0; row < 4; row++) {
for (int column = 3; column >= 0; column--) {
Tile current = board[row][column];
if (current.value == 0) continue;
for (int slot = column + 1; slot < 3; slot++) {
Tile target = board[row][slot];
if (target.value == 0) {
target.value = current.value;
current = target;
board[row][slot - 1].reset();
} else if (target.value == current.value) {
Tile product = target.merge(current);
if (!target.collided && !current.collided) {
current = product;
board[row][slot - 1].reset();
} else {
break;
}
} else {
break;
}
}
}
}
}
I believe something along those lines should work. Sorry if the logic is a bit flawed.
Currently I'm trying to implement heuristics for a 3D tic-tac-toe but it seems like my counter is way of it,f but I'm unsure where I've done wrong, I'm not gonna post all of the code since it's a lot, but here is a part:
public void countDiagonal(GameState gameState) {
/*
* yz-plane (negativ)
*/
int z;
for (int x = 0; x < GameState.BOARD_SIZE; x++) {
for (int y = 0; y < GameState.BOARD_SIZE; y++) {
z = y;
if (gameState.at(x, y, z) == myPlayer) {
myCounter++;
opponentCounter = 0;
}
if (gameState.at(x, y, z) == opponentPlayer) {
opponentCounter++;
myCounter = 0;
}
if (gameState.at(x, y, z) == Constants.CELL_EMPTY) {
emptyCells++;
}
}
evaluateBoard();
myCounter = 0;
opponentCounter = 0;
emptyCells = 0;
}
The evaluation is done here:
public void evaluateBoard() {
if (myCounter == 1 && emptyCells == 3) {
myScore = myScore + 5;
}
if (myCounter == 2 && emptyCells == 2) {
myScore = myScore + 10;
}
if (myCounter == 3 && emptyCells == 1) {
myScore = myScore + 20;
}
if (myCounter == 4) {
myScore = myScore + 1000;
}
if (opponentCounter == 1 && emptyCells == 3) {
opponentScore = opponentScore + 5;
}
if (opponentCounter == 2 && emptyCells == 2) {
opponentScore = opponentScore + 10;
}
if (opponentCounter == 3 && emptyCells == 1) {
opponentScore = opponentScore + 20;
}
if (opponentCounter == 4) {
opponentScore = opponentScore + 1000;
}
}
When I try to run it, I use alpha-beta prune, but it seems like the calculation are done horrbly wrong, when I use the value, I take myScore - opponentScore and I use an alpha-beta tree with depth 1, but even after only playing one move, I'm down -15 in points, as I'm a noob in java, im therefore asking for help, is there an obvious mistake in my way of trying to evaluate the board?
public class TileGrid implements Iterable<Tile> {
private wheelSize = [a positive integer];
private Tile[][] grid = new Tile[wheelSize * 2 + 1][wheelSize * 2 + 1]
#Override
public Iterator<Tile> iterator() {
return ????????;
}
}
I've made a TileGrid class to keep track of a hex-grid for me. It stores Tile-objects in a two dimensional array called grid. Now I want to make the TileGrid class Iterable so that I can easily loop through all Tile-objects. The problem is that there is some positions in the array that is naturally not used (due to the shape of the hex-grid) and thus contains the value null.
My question is this: How do I create an iterator that iterates through all positions in grid except the ones that is null?
I do not want to use some kind of ArrayList since I'm using the array indexes to mark the position of the Tiles.
You have to return an Instance of an Implementation of the Iterator class. The iterator you return should be able to access your array so that the code makes sense.(http://docs.oracle.com/javase/7/docs/api/java/util/Iterator.html)
public Iterator<Tile> iterator() {
return new TileGridIterator(grid);
}
That means you need to write a class that implements the Iterator-Interface and that implements all methods that are specified in the API of that interface.
An example of this could look like that:
import java.util.Iterator;
import java.util.NoSuchElementException;
public class TileGridIterator implements Iterator<Tile> {
int x = 0;
int y = 0;
int nextX = 0;
int nextY = -1;
Tile[][] grid;
public TileGridIterator(Tile[][] grid) {
this.grid = grid;
}
public boolean hasNext() {
while(nextX <= x && nextY < y) {
nextY++;
if(nextY == grid[nextX].length) {
nextY = 0;
nextX++;
}
if(nextX >= grid.length) {
return false;
}
if(grid[nextX][nextY] != null) {
return true;
}
}
if(nextX < grid.length && nextY < grid[nextX].length && grid[nextX][nextY] != null) {
return true;
}
else {
return false;
}
}
public Tile next() {
if(hasNext()) {
x = nextX;
y = nextY;
return grid[x][y];
}else {
throw new NoSuchElementException("no more elements left");
}
}
}
ps: thanks for the question, it was an interesting task for me.
#HopefullyHelpful
My version:
public Iterator<Tile> iterator() {
return new TileIterator(grid);
}
.
class TileIterator implements Iterator<Tile> {
int x = 0, y = -1;
int newX, newY;
Tile[][] grid;
TileIterator(Tile[][] grid) {
this.grid = grid;
updateNewIndex();
}
public boolean hasNext() {
if (newX == -1) {
return false;
}
return true;
}
public Tile next() {
x = newX;
y = newY;
updateNewIndex();
if (x == -1) {
throw new NoSuchElementException("no more elements left");
}
return grid[x][y];
}
private void updateNewIndex() {
newX = x;
newY = y;
do {
newY++;
if (newY == grid[newX].length) {
newY = 0;
newX = newX + 1;
if (newX == grid.length) {
newX = newY = -1;
}
}
} while (newX != -1 && grid[newX][newY] == null);
}
}
Thanks again for your answer as it helped me make this.
public static boolean diagonals(char[][] b, int row, int col, int l) {
int counter = 1; // because we start from the current position
char charAtPosition = b[row][col];
int numRows = b.length;
int numCols = b[0].length;
int topleft = 0;
int topright = 0;
int bottomleft = 0;
int bottomright = 0;
for (int i=row-1,j=col-1;i>=0 && j>=0;i--,j--) {
if (b[i][j]==charAtPosition) {
topleft++;
} else {
break;
}
}
for (int i=row-1,j=col+1;i>=0 && j<=numCols;i--,j++) {
if (b[i][j]==charAtPosition) {
topright++;
} else {
break;
}
}
for (int i=row+1,j=col-1;i<=numRows && j>=0;i++,j--) {
if (b[i][j]==charAtPosition) {
bottomleft++;
} else {
break;
}
}
for (int i=row+1,j=col+1;i<=numRows && j<=numCols;i++,j++) {
if (b[i][j]==charAtPosition) {
bottomright++;
} else {
break;
}
}
return topleft + bottomright + 1 >= l || topright + bottomleft + 1 >= l; //in this case l is 5
}
After I was done posting the code above here, I couldn't help but wanted to simplify the code by merging the four pretty much the same loops into one method.
Here's the kind of method I want to have:
public int countSteps(char horizontal, char vertical) {
}
Two parameters horizontal and vertical can be either + or - to indicate the four directions to walk in. What I want to see if possible at all is i++; is generalized to i horizontal horizontal; when horizontal taking the value of +.
What I don't want to see is if or switch statements, for example:
public int countSteps(char horizontal, char vertical) {
if (horizontal == '+' && vertical == '-') {
for (int i=row-1,j=col+1;i>=0 && j<=numCols;i--,j++) {
if (b[i][j]==charAtPosition) {
topright++;
} else {
break;
}
}
} else if (horizontal == '+' && vertical == '+') {
for (int i=row+1,j=col+1;i>=0 && j<=numCols;i++,j++) {
if (b[i][j]==charAtPosition) {
topright++;
} else {
break;
}
}
} else if () {
} else {
}
}
Since it is as tedious as the original one. Note also that the comparing signs for the loop condition i>=0 && j<=numCols; for example, >= && <= have correspondence with the value combination of horizontal and vertical.
Sorry for my bad wording, please let me know if anything is not clear.
You can easily convert the loops to something like:
int doit(int i_incr, int j_incr) {
int cornerIncrement = 0;
for (int i=row+i_incr, j=col+j_incr; i>=0 && j>=0; i+=i_incr, j+=j_incr) {
if (b[i][j]==charAtPosition) {
cornerIncrement++;
} else {
break;
}
}
return cornerIncrement;
}
And then repeat 4 times...
int increment = doit(+1, -1); // Or (-1, +1) etc
topLeft += increment; // Or bottomLeft/topRight/bottomRight
So you have these two loops:
for (int i=row-1,j=col-1;i>=0 && j>=0;i--,j--) {
if (b[i][j]==charAtPosition) {
topleft++;
} else {
break;
}
}
for (int i=row-1,j=col+1;i>=0 && j<=numCols;i--,j++) {
if (b[i][j]==charAtPosition) {
topright++;
} else {
break;
}
}
First, turn your counters into an array, i.e. topleft -> counter[0] and topright -> counter[1]
Then, turn the difference between the code into variables, so you have:
for(direction = 0; direction < 2; direction++) {
int offset = direction * 2 - 1; // This is what I mean by doing some math
for(int i=row-1,j=col+offset;i>=0 && -j*offset>=-numCols*direction;i--,j+=offset) {
if (b[i][j]==charAtPosition) {
counter[direction]++;
// etc.
The math can get ugly sometimes, or you can do it in a separate line. Look at my other post on this question for an elegant way to do the math in this particular problem using ? : syntax.
I'm creating a simple engine that plays Othello, using minimax with alpha beta cuts.
It's playing well, but sometimes i get a weird index out of bounds exception (near the
endgame, always).
Here' my algorithm
private float minimax(OthelloBoard board, OthelloMove best, float alpha, float beta, int depth)
{
calls++;
float bestResult = -Float.MAX_VALUE;
OthelloMove garbage = new OthelloMove();
int state = board.getState();
int currentPlayer = board.getCurrentPlayer();
if (state == OthelloBoard.STATE_DRAW)
return 0.0f;
if ((state == OthelloBoard.STATE_BLACK_WINS) && (currentPlayer == OthelloBoard.BLACK))
return Float.MAX_VALUE;
if ((state == OthelloBoard.STATE_WHITE_WINS) && (currentPlayer == OthelloBoard.WHITE))
return Float.MAX_VALUE;
if ((state == OthelloBoard.STATE_BLACK_WINS) && (currentPlayer == OthelloBoard.WHITE))
return -Float.MAX_VALUE;
if ((state == OthelloBoard.STATE_WHITE_WINS) && (currentPlayer == OthelloBoard.BLACK))
return -Float.MAX_VALUE;
if (depth == maxDepth)
return OthelloHeuristics.eval(currentPlayer, board);
ArrayList<OthelloMove> moves = board.getAllMoves(currentPlayer);
for (OthelloMove mv : moves)
{
board.makeMove(mv);
alpha = - minimax(board, garbage, -beta, -alpha, depth + 1);
board.undoMove(mv);
if (beta <= alpha)
return alpha;
if (alpha > bestResult)
{
best.setFlipSquares(mv.getFlipSquares());
best.setIdx(mv.getIdx());
best.setPlayer(mv.getPlayer());
bestResult = alpha;
}
}
return bestResult;
}
Inside makeMove and undoMove i update the game state(black wins, white wins, draw).
I also toggle the players inside these methods. When a player has no moves i make a dummy
move without changing the board, and toggle the players.
There's a lot more of code, but i think the problem happens when the algorithm hits the
game over position. This problem doesn't happen when i set the engine to play random moves, so the problem should be the alpha beta algorithm.
Here is getAllMoves, this call getFlips:
public ArrayList<OthelloMove> getAllMoves(int player)
{
ArrayList<OthelloMove> moves = new ArrayList<OthelloMove>();
for (int i = 10; i < 90; i++)
{
int col = i % 10;
if (col != 0 && col != 9)
{
if (cells[i] == EMPTY)
{
ArrayList<Integer> flips = getFlips(i, player);
if (flips.size() > 0)
{
OthelloMove mv = new OthelloMove();
mv.setFlipSquares(flips);
mv.setIdx(i);
mv.setPlayer(player);
moves.add(mv);
}
}
}
}
return moves;
}
Here is getFlips.
public ArrayList<Integer> getFlips(int idx, int player)
{
int opponent = getOpponent(player);
ArrayList<Integer> flips = new ArrayList<Integer>();
if (cells[idx] != EMPTY)
return flips;
for (Integer dir : DIRECTIONS)
{
int distance = 1;
int tempIdx = idx;
while (cells[tempIdx += dir] == opponent)
distance++;
if ((cells[tempIdx] == player) && (distance > 1))
{
while (distance-- > 1)
{
tempIdx -= dir;
flips.add(tempIdx);
}
}
}
return flips;
}
Here is updateState:
public void updateState()
{
int opponent = getOpponent(currentPlayer);
int playerMoves = getAllMoves(currentPlayer).size();
int opponentMoves = getAllMoves(opponent).size();
if ( ((playerMoves == 0) && (opponentMoves == 0)) || (emptyCells == 0))
{
int blackDiscs = countDiscs(BLACK);
int whiteDiscs = countDiscs(WHITE);
if (blackDiscs > whiteDiscs)
state = STATE_BLACK_WINS;
else if (blackDiscs < whiteDiscs)
state = STATE_WHITE_WINS;
else
state = STATE_DRAW;
}
}
Thanks!
I am not familiar with the game specifically, but I believe it has something to do with the fact hat in the line:
while (cells[tempIdx += dir] == opponent)
You should also check you are not out of bound, otherwise - if there is still an opponent on the end of the board, you will keep increasing dir
Try changing this line to:
while (tempIdx + dir >= 0 && tempIdx + dir < cells.length && cells[tempIdx += dir] == opponent)
As a rule of thumb, usually it is a good practice in array accesses, especially in loops, to guard against going out of bound by checking the length explicitly.
Found the problem, thanks anyway.
The bug was a situation where a player can't move, and must pass the turn.
The tricky is to play a 'ghost move' (i.e. a move that doesn't change the board), and
toggle the players turn, so that the Minimax doesn't even notice this situation.
I was doing this, but in the wrong place! The code is like:
public void makeMove (OthelloMove move)
{
int player = move.getPlayer();
ArrayList<Integer> flips = move.getFlipSquares();
if (flips != null)
{
int idx = move.getIdx();
cells[idx] = player;
for (Integer flip : flips)
cells[flip] = player;
emptyCells--;
this.updatePhase();
}
this.toogleCurrentPlayer();
}
public void undoMove (OthelloMove move)
{
int player = move.getPlayer();
ArrayList<Integer> flips = move.getFlipSquares();
int opponent = getOpponent(player);
if (flips != null)
{
int idx = move.getIdx();
cells[idx] = EMPTY;
for (Integer flip : flips)
cells[flip] = opponent;
emptyCells++;
this.updatePhase();
}
this.toogleCurrentPlayer();
}