Currently i'm creating tic-tac-toe-game in android
i'm successfully created game but facing some issues
Here is my code that i have tried so far
Here is My BoardView
public class BoardView extends View implements GestureDetector.OnGestureListener, ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
private static final int STROKE_WIDTH = 10;
private static final int SWEEPER_WIDTH = 20;
private float[] gridLinePoints;
private Paint gridPaint;
private PointF[][] centerPoints;
private Paint signPaint;
private List<SignData> signDataList;
private #Constants.WinLinePosition int winLinePosition;
private Paint winLinePaint;
private GestureDetector clickDetector;
private OnBoardInteractionListener onBoardInteractionListener;
private ValueAnimator clickAnimator;
private ValueAnimator winLineAnimator;
private ValueAnimator resetAnimator;
private float signRadius;
private float winLineLength;
private float sweeperStartPosition;
private Paint sweeperPaint;
private int[] sweeperColors;
private float[] sweeperStops;
public BoardView(Context context) {
super(context);
init();
}
public BoardView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BoardView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#TargetApi(Build.VERSION_CODES.M)
private void init() {
gridLinePoints = new float[16];
centerPoints = new PointF[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
centerPoints[i][j] = new PointF();
}
}
signDataList = new ArrayList<>();
winLinePosition = Constants.NONE;
gridPaint = new Paint();
gridPaint.setColor(getContext().getResources().getColor(R.color.holo_green_dark, null));
gridPaint.setAntiAlias(true);
gridPaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
gridPaint.setStrokeCap(Paint.Cap.ROUND);
signPaint = new Paint();
signPaint.setColor(getContext().getResources().getColor(R.color.holo_orange_dark, null));
signPaint.setAntiAlias(true);
signPaint.setStyle(Paint.Style.STROKE);
signPaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
signPaint.setStrokeCap(Paint.Cap.ROUND);
winLinePaint = new Paint();
winLinePaint.setColor(getContext().getResources().getColor(R.color.holo_red_dark, null));
winLinePaint.setAntiAlias(true);
winLinePaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
winLinePaint.setStrokeCap(Paint.Cap.ROUND);
clickDetector = new GestureDetector(getContext(), this);
clickAnimator = new ValueAnimator();
clickAnimator.setDuration(150);
clickAnimator.setInterpolator(new DecelerateInterpolator());
clickAnimator.addUpdateListener(this);
clickAnimator.addListener(this);
winLineAnimator = new ValueAnimator();
winLineAnimator.setDuration(150);
winLineAnimator.setInterpolator(new DecelerateInterpolator());
winLineAnimator.addUpdateListener(this);
winLineAnimator.addListener(this);
resetAnimator = new ValueAnimator();
resetAnimator.setDuration(500);
resetAnimator.setInterpolator(new AccelerateInterpolator());
resetAnimator.addUpdateListener(this);
resetAnimator.addListener(this);
sweeperPaint = new Paint();
sweeperPaint.setAntiAlias(true);
sweeperPaint.setStyle(Paint.Style.FILL);
sweeperColors = new int[3];
sweeperColors[0] = Color.parseColor("#0000DDFF");
sweeperColors[1] = Color.parseColor("#FF00DDFF");
sweeperColors[2] = Color.parseColor("#0000DDFF");
sweeperStops = new float[3];
sweeperStops[0] = 0;
sweeperStops[1] = 0.5f;
sweeperStops[2] = 1;
setLayerType(LAYER_TYPE_SOFTWARE, sweeperPaint);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
getLayoutParams().height = getMeasuredWidth();
setGridLinePoints();
setCenterPoints();
setAnimationValues();
}
#Override
protected void onDraw(Canvas canvas) {
drawGrid(canvas);
super.onDraw(canvas);
if (resetAnimator.isRunning()) {
canvas.clipRect(0, sweeperStartPosition, getMeasuredWidth(), getMeasuredWidth());
setSweeperGradient();
canvas.drawRect(0, sweeperStartPosition, getMeasuredWidth(), sweeperStartPosition + dpToPx(SWEEPER_WIDTH), sweeperPaint);
}
drawSigns(canvas);
drawWinLine(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if ((!isEnabled()) || (clickAnimator.isRunning()) || (isAnimationFlagSet())) {
return super.onTouchEvent(event);
} else {
return clickDetector.onTouchEvent(event);
}
}
private boolean isAnimationFlagSet() {
for (SignData signData : signDataList) {
if (signData.isAnimationFlag()) {
return true;
}
}
return false;
}
private void setGridLinePoints() {
int side = getMeasuredWidth();
float padding = dpToPx(STROKE_WIDTH / 2f);
gridLinePoints[0] = gridLinePoints[4] = gridLinePoints[9] = gridLinePoints[13] = padding;
gridLinePoints[1] = gridLinePoints[3] = gridLinePoints[8] = gridLinePoints[10] = side / 3f;
gridLinePoints[2] = gridLinePoints[6] = gridLinePoints[11] = gridLinePoints[15] = side - padding;
gridLinePoints[5] = gridLinePoints[7] = gridLinePoints[12] = gridLinePoints[14] = (2 * side) / 3f;
}
private void setCenterPoints() {
float a = getMeasuredWidth() / 6f;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
centerPoints[i][j].x = a + (j * (2 * a));
centerPoints[i][j].y = a + (i * (2 * a));
}
}
}
private void setAnimationValues() {
clickAnimator.setFloatValues(0, (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH));
winLineAnimator.setFloatValues(0, getMeasuredWidth());
resetAnimator.setFloatValues(-dpToPx(SWEEPER_WIDTH), getMeasuredWidth());
}
private void setSweeperGradient() {
float axis = sweeperStartPosition + (dpToPx(SWEEPER_WIDTH / 2f));
LinearGradient horizontalGradient = new LinearGradient(0, axis, getMeasuredWidth(), axis,
sweeperColors, sweeperStops, Shader.TileMode.CLAMP);
LinearGradient verticalGradient = new LinearGradient(getMeasuredWidth() / 2f, sweeperStartPosition,
getMeasuredWidth() / 2f, sweeperStartPosition + dpToPx(SWEEPER_WIDTH), sweeperColors, sweeperStops,
Shader.TileMode.CLAMP);
ComposeShader shader = new ComposeShader(horizontalGradient, verticalGradient, PorterDuff.Mode.MULTIPLY);
sweeperPaint.setShader(shader);
}
private void drawGrid(Canvas canvas) {
canvas.drawLines(gridLinePoints, gridPaint);
}
private void drawSigns(Canvas canvas) {
for (int i = 0; i < signDataList.size(); i++) {
SignData signData = signDataList.get(i);
switch (signData.getSign()) {
case Constants.CIRCLE:
drawCircle(canvas, centerPoints[signData.getRow()][signData.getColumn()], signData.isAnimationFlag());
break;
case Constants.CROSS:
drawCross(canvas, centerPoints[signData.getRow()][signData.getColumn()], signData.isAnimationFlag());
break;
case Constants.EMPTY:
break;
}
}
}
private void drawCircle(Canvas canvas, PointF center, boolean animationFlag) {
float radius = animationFlag ? signRadius : (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH);
canvas.drawCircle(center.x, center.y, radius, signPaint);
}
private void drawCross(Canvas canvas, PointF center, boolean animationFlag) {
float radius = animationFlag ? signRadius : (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH);
canvas.drawLine(center.x - radius, center.y - radius, center.x + radius, center.y + radius, signPaint);
canvas.drawLine(center.x - radius, center.y + radius, center.x + radius, center.y - radius, signPaint);
}
private void drawWinLine(Canvas canvas) {
float length = winLineLength;
float a = getMeasuredWidth() / 6f;
float padding = dpToPx(STROKE_WIDTH);
switch (winLinePosition) {
case Constants.NONE:
break;
case Constants.ROW_1:
canvas.drawLine(padding, a, length - padding, a, winLinePaint);
break;
case Constants.ROW_2:
canvas.drawLine(padding, a + (2 * a), length - padding, a + (2 * a), winLinePaint);
break;
case Constants.ROW_3:
canvas.drawLine(padding, a + (4 * a), length - padding, a + (4 * a), winLinePaint);
break;
case Constants.COLUMN_1:
canvas.drawLine(a, padding, a, length - padding, winLinePaint);
break;
case Constants.COLUMN_2:
canvas.drawLine(a + (2 * a), padding, a + (2 * a), length - padding, winLinePaint);
break;
case Constants.COLUMN_3:
canvas.drawLine(a + (4 * a), padding, a + (4 * a), length - padding, winLinePaint);
break;
case Constants.DIAGONAL_1:
canvas.drawLine(padding, padding, length - padding, length - padding, winLinePaint);
break;
case Constants.DIAGONAL_2:
canvas.drawLine(getMeasuredWidth() - padding, padding, padding + getMeasuredWidth()
- length, length - padding, winLinePaint);
break;
}
}
void addSignToBoard(#Constants.Sign int sign, int row, int column) {
SignData signData = new SignData();
signData.setSign(sign);
signData.setRow(row);
signData.setColumn(column);
signData.setAnimationFlag(true);
if (clickAnimator.isRunning()) {
clickAnimator.end();
}
signDataList.add(signData);
clickAnimator.start();
}
void showWinLine(#Constants.WinLinePosition int winLinePosition) {
this.winLinePosition = winLinePosition;
winLineAnimator.start();
}
void resetBoard() {
if (!resetAnimator.isRunning()) {
resetAnimator.start();
}
}
boolean isAlreadyAdded(int row, int column) {
for (int i = 0; i < signDataList.size(); i++) {
SignData signData = signDataList.get(i);
if ((signData.getRow() == row) && (signData.getColumn() == column)) {
return true;
}
}
return false;
}
private float dpToPx(float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
}
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public void onShowPress(MotionEvent e) {
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
float x = e.getX();
float y = e.getY();
int row = detectIndexOfPartition(y);
int column = detectIndexOfPartition(x);
if ((row != -1) && (column != -1)) {
onBoardInteractionListener.onBoardClick(BoardView.this, row, column);
}
return true;
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
#Override
public void onLongPress(MotionEvent e) {
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
private int detectIndexOfPartition(float value) {
float maxValue = getMeasuredWidth();
float totalNumberOfPartitions = 3;
float lengthOfSinglePartition = maxValue / totalNumberOfPartitions;
return (int) (value / lengthOfSinglePartition);
}
public void setOnBoardInteractionListener(OnBoardInteractionListener onBoardInteractionListener) {
this.onBoardInteractionListener = onBoardInteractionListener;
}
#Override
public void onAnimationUpdate(ValueAnimator animation) {
if (animation == clickAnimator) {
signRadius = (float) animation.getAnimatedValue();
} else if (animation == winLineAnimator) {
winLineLength = (float) animation.getAnimatedValue();
} else if (animation == resetAnimator) {
sweeperStartPosition = (float) animation.getAnimatedValue();
}
invalidate();
}
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
if (animation == clickAnimator) {
SignData signData = signDataList.get(signDataList.size() - 1);
signData.setAnimationFlag(false);
onBoardInteractionListener.onSignAdded(signData.getSign(), signData.getRow(), signData.getColumn());
signRadius = 0;
} else if (animation == resetAnimator) {
signDataList.clear();
winLinePosition = Constants.NONE;
onBoardInteractionListener.onBoardReset();
}
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
interface OnBoardInteractionListener {
void onBoardClick(BoardView board, int row, int column);
void onSignAdded(#Constants.Sign int sign, int row, int column);
void onBoardReset();
}
private class SignData {
private #Constants.Sign int sign;
private int row;
private int column;
private boolean animationFlag;
#Constants.Sign int getSign() {
return sign;
}
void setSign(#Constants.Sign int sign) {
this.sign = sign;
}
int getRow() {
return row;
}
void setRow(int row) {
this.row = row;
}
int getColumn() {
return column;
}
void setColumn(int column) {
this.column = column;
}
boolean isAnimationFlag() {
return animationFlag;
}
void setAnimationFlag(boolean animationFlag) {
this.animationFlag = animationFlag;
}
}
}
My Brain class
class Brain {
private static Brain INSTANCE;
private #Constants.Sign
int[][] board = new int[3][3];
private int rowOfResult;
private int columnOfResult;
private int depth;
private #Constants.Sign
int computerSign;
private #Constants.Sign
int playerSign;
private OnProcessCompleteListener onProcessCompleteListener;
private static final int HORIZONTAL = 0;
private static final int VERTICAL = 1;
private static final int DIAGONAL = 2;
#IntDef({HORIZONTAL, VERTICAL, DIAGONAL})
#interface DirectionOfWinLine {
}
// References used by isWin function.
private int[] winSequence = new int[3];
private int[] row = new int[3];
private int[] column = new int[3];
private int[] diagonal1 = new int[3];
private int[] diagonal2 = new int[3];
private Brain() {
}
static Brain getInstance() {
if (INSTANCE == null) {
INSTANCE = new Brain();
}
return INSTANCE;
}
void play() {
if (onProcessCompleteListener == null) {
return;
}
calculateNextMove(computerSign, depth);
onProcessCompleteListener.onNextMoveCalculated(rowOfResult, columnOfResult);
}
private int calculateNextMove(#Constants.Sign int sign, int depth) {
if (isWin(computerSign, false)) {
return 10 - depth;
} else if (isWin(playerSign, false)) {
return depth - 10;
}
if (depth >= 9) {
return 0;
}
List<Integer> scores = new ArrayList<>(), rowIndices = new ArrayList<>(), columnIndices = new ArrayList<>();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == Constants.EMPTY) {
board[i][j] = sign;
scores.add(calculateNextMove(getOppositeSign(sign), depth + 1));
rowIndices.add(i);
columnIndices.add(j);
board[i][j] = Constants.EMPTY;
}
}
}
if (sign == computerSign) {
int maxScore = -100;
for (int i = 0; i < scores.size(); i++) {
if (scores.get(i) > maxScore) {
maxScore = scores.get(i);
}
}
return randomizeScore(maxScore, scores, rowIndices, columnIndices);
} else {
int minScore = 100;
for (int i = 0; i < scores.size(); i++) {
if (scores.get(i) < minScore) {
minScore = scores.get(i);
}
}
return randomizeScore(minScore, scores, rowIndices, columnIndices);
}
}
private int randomizeScore(int score, List<Integer> scores, List<Integer> rowIndices, List<Integer> columnIndices) {
List<Integer> equalScoreIndices = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
if (scores.get(i) == score) {
equalScoreIndices.add(i);
}
}
Random rand = new Random();
int randomIndex = equalScoreIndices.get(rand.nextInt(equalScoreIndices.size()));
rowOfResult = rowIndices.get(randomIndex);
columnOfResult = columnIndices.get(randomIndex);
return score;
}
private boolean isWin(#Constants.Sign int sign, boolean notifyWinEnabled) {
for (int i = 0; i < 3; i++) {
winSequence[i] = sign;
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j) {
diagonal1[i] = board[i][j];
}
if ((i + j) == 2) {
diagonal2[i] = board[i][j];
}
row[j] = board[i][j];
column[j] = board[j][i];
}
if (isEqual(row, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, HORIZONTAL, i + 1);
}
return true;
} else if (isEqual(column, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, VERTICAL, i + 1);
}
return true;
}
}
if (isEqual(diagonal1, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, DIAGONAL, 1);
}
return true;
} else if (isEqual(diagonal2, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, DIAGONAL, 2);
}
return true;
}
return false;
}
private boolean isEqual(int[] x, int[] y) {
for (int i = 0; i < 3; i++) {
if (x[i] != y[i]) {
return false;
}
}
return true;
}
void analyzeBoard() {
if (onProcessCompleteListener == null) {
return;
}
if ((!isWin(Constants.CIRCLE, true)) && (!isWin(Constants.CROSS, true)) && (depth >= 9)) {
onProcessCompleteListener.onGameDraw();
}
}
private void notifyWin(#Constants.Sign int sign, #DirectionOfWinLine int direction, int index) {
if (onProcessCompleteListener == null) {
return;
}
#Constants.WinLinePosition int winLinePosition = Constants.NONE;
switch (direction) {
case HORIZONTAL:
switch (index) {
case 1:
winLinePosition = Constants.ROW_1;
break;
case 2:
winLinePosition = Constants.ROW_2;
break;
case 3:
winLinePosition = Constants.ROW_3;
break;
}
break;
case VERTICAL:
switch (index) {
case 1:
winLinePosition = Constants.COLUMN_1;
break;
case 2:
winLinePosition = Constants.COLUMN_2;
break;
case 3:
winLinePosition = Constants.COLUMN_3;
break;
}
break;
case DIAGONAL:
switch (index) {
case 1:
winLinePosition = Constants.DIAGONAL_1;
break;
case 2:
winLinePosition = Constants.DIAGONAL_2;
break;
}
break;
}
onProcessCompleteListener.onGameWin(sign, winLinePosition);
}
void reset() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
board[i][j] = Constants.EMPTY;
}
}
depth = 0;
}
void setComputerSign(int computerSign) {
this.computerSign = computerSign;
playerSign = getOppositeSign(computerSign);
}
void updateBoard(#Constants.Sign int sign, int row, int column) {
board[row][column] = sign;
depth++;
}
private #Constants.Sign
int getOppositeSign(#Constants.Sign int sign) {
return sign == Constants.CIRCLE ? Constants.CROSS : Constants.CIRCLE;
}
void setOnProcessCompleteListener(OnProcessCompleteListener onProcessCompleteListener) {
this.onProcessCompleteListener = onProcessCompleteListener;
}
interface OnProcessCompleteListener {
void onNextMoveCalculated(int row, int column);
void onGameWin(#Constants.Sign int sign, #Constants.WinLinePosition int winLinePosition);
void onGameDraw();
}
void destroy() {
INSTANCE = null;
}
}
I have created github repo for this all code available here
https://github.com/SuperSaiyanGoku3/MyGame
I'm facing some issue in above code
The Above code only support hard level (Impossible), how can i create easy medium and hard level in above game algorithm again computer(CPU).
how can i set custom icon instead of O and X
how can i set all three game mode randomly in above code, as easy medium and hard level, so when user start the game, cpu will come randomly, wither easy or medium or hard.
Here are some links that i have tried so far but unable to understand how to create easy medium and hard level
Learn to create a Tic-Tac-Toe Game for Android
Android Studio - JAVA - TIC TAC TOE
Android tic tac toe game - logic issue
Tic-Tac-Toe (TableLayout) Android App
Tic Tac Toe Game
If need more information please do let me know. Thanks in advance. Your efforts will be appreciated.
Implement difficulty: For support easy and medium difficulty, I suggest you just use random. You have implemented HARD difficulty, so you just need lesser difficult logic that making "mistakes", and this mistake can be implemented by random.
private void calculateNextMoveRandom() {
Random rand = new Random();
int randomRow;
int randomColumn;
while (true) {
randomRow = rand.nextInt(3);
randomColumn = rand.nextInt(3);
if (Constants.EMPTY == board[randomRow][randomColumn]) {
rowOfResult = randomRow;
columnOfResult = randomColumn;
return;
}
}
}
2. **Bitmap marker:** You can use `BitmapFactory.decodeResource()` to draw bitmap on screen.
private void drawCircle(Canvas canvas, PointF center, boolean animationFlag) {
int iconSize = (int) LayoutUtil.getPixelFromDp(MARKER_SIZE, getContext());
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android);
Bitmap scaled = Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, true);
canvas.drawBitmap(scaled, center.x - (iconSize >> 1), center.y - (iconSize >> 1), signPaint);
}
3. **Random difficulty:** Just use random to set difficulty.
brain.setDifficulty(new Random().nextInt(3));
Here is my pull request: (Fallen link)
https://github.com/SuperSaiyanGoku3/MyGame/pull/1
If you are using a minimax strategy to play tic-tac-toe, you can have multiple switch cases that correspond to different difficulty levels in the game. The most naive method of doing this is by putting different depth thresholds in the minimax tree. For example, you can expand the minimax game tree until only depth 2 (assuming for a 3*3 tic-tac-toe) for easy level and say until the end of the search tree for the highest difficulty level. You can set this threshold depending on the board size and the difficulty level expected.
Another way of implementing difficulty levels is by implementing different heuristic functions to calculate the board score (a goodness measure to the current state of the board). You can have your heuristic function that evaluates the individual row/column/diagonal score based on the number of cells that you occupy in that row/column/diagonal. For example, if the number of cells occupied is 1, then score = x (x can be any arbitrary number). If the number of cells occupied is 2, then score = x^2 and if it is 3, then score = x^3. Then multiply all individual line scores to get a measure of goodness (or you can think of this as a winning probability) in each row, column or diagonal. You can calculate a similar score for your opponent and then take a difference to get a board score. Thus the idea is to implement different ways of evaluating current board states by designing different variations of the heuristic function for each difficulty level. A bad heuristic function would work as an easy level since it will lose a lot of games whereas a properly designed heuristic will have a no-lose policy (it will end up winning or drawing the game).
In order to randomly choose a difficulty level at each run of the game, you can use a random number generator to pick a difficulty level. You can assign '0' as easy, '1' as medium and '2' as hard. You can use the following java function to generate an integer between 0 and (n-1)
int random = Random.nextInt(n)
Complete Tic Tac Toe with "n x n matrix"
Basic ideas is that we just have to check if column / row / diagonal elements are same.
code written in C#, with basic unit test.
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;
}