I'm currently trying to implement Conway's Game of Life in Java. On a first glance the application seems to work as it either stalls in the states that are expected as stalling states (e.g. block, bee-hive and tub) or changes infinitely.
However, as I took a closer look I never actually saw a Blinker or Beacon nor a Loaf (for reference images of these states see here). So I decided to implement a drawing mode and a one-step-update method that gives me the possibility to analyze exactly what's going on.
The strange thing that I found was the following:
When entering Drawing Mode I reset all cells and all buffered cells on the board to dead. Then, I drew a vertical Blinker somewhere in the middle of the board and did one update step and checked for the update mechanism of all cells that are alive (which are only the 3 cells belonging to the Blinker). In the output however, all of them returned that the value to their bottom left was alive (which actually could not be the case because nothing is being logged for these cells).
I'm really confused about where I went wrong in my code because this just doesn't make any sense for me. The values for the next generation are being stored in a buffer and only written back after the next generation has been calculated entirely, so I don't think that this should be the issue.
For reproducibility, here is the code:
Main.java
import java.awt.EventQueue;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
createAndShowUI();
});
}
private static void createAndShowUI() {
AnimationPanel panel = new AnimationPanel();
JFrame frame = new JFrame("Game of Life");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
super.keyReleased(e);
if(e.getKeyCode() == e.VK_R) {
panel.reset();
panel.startAnimation();
}
if(e.getKeyCode() == e.VK_C) {
panel.clear();
}
if(e.getKeyCode() == e.VK_S) {
panel.startAnimation();
}
if(e.getKeyCode() == e.VK_SPACE) {
panel.stepAnimation();
}
}
});
frame.setVisible(true);
panel.populate();
panel.startAnimation();
}
}
AnimationPanel.java:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JPanel;
import javax.swing.Timer;
public class AnimationPanel extends JPanel implements ActionListener {
private static final long serialVersionUID = 4929258196354814855L;
private int width = 1920;
private int height = 1080;
private int gridSize = 10;
private Cell[][] cells;
private Cell[][] buffer;
private Timer timer;
private boolean drawingMode = false;
public AnimationPanel() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if(drawingMode) {
int x = e.getX();
int y = e.getY();
int xIndex = (int)x / gridSize;
int yIndex = (int)y / gridSize;
cells[xIndex][yIndex].live = !cells[xIndex][yIndex].live;
repaint();
}
}
});
}
public void reset() {
timer.stop();
cells = null;
buffer = null;
populate();
}
public void clear() {
timer.stop();
for(Cell[] row : cells) {
for(Cell cell: row) {
cell.live = false;
}
}
for(Cell[] row : buffer) {
for(Cell cell: row) {
cell.live = false;
}
}
repaint();
drawingMode = true;
}
public void populate() {
Random random = new Random();
int horizontalCellCount = width / gridSize;
int verticalCellCount = height / gridSize;
cells = new Cell[horizontalCellCount][verticalCellCount];
buffer = new Cell[horizontalCellCount][verticalCellCount];
for(int i = 0; i < horizontalCellCount; i++) {
for(int j = 0; j < verticalCellCount; j++) {
double randVal = random.nextDouble();
boolean live = (randVal < 0.05) ? true : false;
int x = i * gridSize;
int y = j * gridSize;
cells[i][j] = new Cell(live, x, y, gridSize);
buffer[i][j] = new Cell(live, x, y, gridSize);
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(Cell[] row : cells) {
for(Cell cell : row) {
cell.draw(g);
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public void startAnimation() {
drawingMode = false;
timer = new Timer(250, this);
timer.start();
}
public void stepAnimation() {
update();
repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
private void update() {
int horizontalCellCount = width / gridSize;
int verticalCellCount = height / gridSize;
boolean log = false;
for(int i = 0; i < horizontalCellCount; i++) {
for(int j = 0; j < verticalCellCount; j++) {
if(cells[i][j].live) {
log = true;
System.out.println("Cell: (" + i + "|" + j + ") " + cells[i][j].toString());
System.out.println("Row length: " + cells[i].length);
System.out.println("Column length: " + cells.length);
} else {
log = false;
}
int liveNeighours = 0;
for(int x = i - 1; x <= i + 1; x++) {
if(x < 0 || x == horizontalCellCount) {
continue;
}
for(int y = j - 1; y <= j + 1; y++) {
if(y < 0 || y == verticalCellCount || y == j && x == i) {
continue;
}
if(cells[x][y].live) {
liveNeighours++;
}
if(log) {
System.out.println("Cell at " + x + "|" + y + ": " + cells[x][y].live + " - liveNeighbours: " + liveNeighours);
}
}
}
Cell cell = cells[i][j];
if(liveNeighours > 3 || liveNeighours < 2) {
buffer[i][j].live = false;
} else if(!cell.live && liveNeighours == 3) {
buffer[i][j].live = true;
}
}
}
for(int i = 0; i < horizontalCellCount; i++) {
for(int j = 0; j < verticalCellCount; j++) {
cells[i][j] = buffer[i][j];
}
}
}
}
Cell.java
import java.awt.Color;
import java.awt.Graphics;
public class Cell {
public boolean live;
public int x;
public int y;
public int size;
public Cell(boolean live, int x, int y, int size) {
this.live = live;
this.x = x;
this.y = y;
this.size = size;
}
public void draw(Graphics g) {
if(live) {
g.setColor(Color.BLACK);
} else {
g.setColor(Color.WHITE);
}
g.fillRect(x, y, size, size);
}
#Override
public String toString() {
return "(" + x + " " + y + ") - Size: " + size;
}
}
Any help is really appreciated on this as I'm really confused what is wrong with the code.
Related
I am writing a video game GUI and I want to firstly open a frame with a menu bar and when the user clicks on play in the menu a JPanel that is in a different class gets added to the current one and it ends up with a frame containing the menu bar and the JPanel. When I run the code bellow I don't get any errors and the console initiates the process. The problem is nothing shows on the screen?? Not the initial frame or anything else.
The code for the frame that calls the class with the JPanel is:
/*-----------------------------------------------------------------------------------------------------*/
package Testes;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Tetris extends JFrame{
private static final long serialVersionUID = 1L;
public static final int WIDTH = 250;
public static final int HEIGHT = 490;
public Tetris() {
JFrame frame = new JFrame();
TetrisBoard janela = new TetrisBoard();
JMenuBar menubar = new JMenuBar();
JMenu start = new JMenu("Start");
JMenuItem play = new JMenuItem("Play");
play.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
janela.startGame();
}
});
start.add(play);
JMenuItem exit = new JMenuItem("Exit");
exit.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
});
start.add(exit);
JMenu help = new JMenu("Help");
JMenuItem manual = new JMenuItem("User Manual");
manual.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(null, "The goal of Tetris is to eliminate \nas many lines as possible before\n the Tetrominoes reach the top.\nControls:\n\u2190 - Move Left\n\u2192 - Move Right\n\u2193 - Drop\n" +
"C - Rotate AntiClockwise\nV - Rotate Clockwise\nP - Pause\nEsc - Quit","Instructions", JOptionPane.OK_OPTION, new ImageIcon());
}
});
help.add(manual);
JMenuItem about = new JMenuItem("About");
about.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JLabel label = new JLabel("<html><center>Tetris made by GG<br>MEEC<br>2020<html>");
label.setHorizontalAlignment(SwingConstants.CENTER);
JOptionPane.showMessageDialog(null, label, "About", JOptionPane.INFORMATION_MESSAGE);
}
});
help.add(about);
menubar.add(start);
menubar.add(help);
frame.add(janela,BorderLayout.CENTER);
frame.add(menubar,BorderLayout.NORTH);
janela.setFocusable(true);
frame.setTitle("Tetris");
frame.setLayout(new BorderLayout());
frame.setSize(250, 490);
//setPreferredSize(new Dimension(255, 495));
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.pack();
}
public static void main(String[] args) {
new Tetris();
}
}
/*-----------------------------------------------------------------------------------------------------*/
And the class with the JPanel is:
/*-----------------------------------------------------------------------------------------------------*/
package Testes;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
public class TetrisBoard extends JPanel implements KeyListener/*, ActionListener*/{
private static final long serialVersionUID = 1L;
public static final int COLOR_MIN = 35;
public static final int COLOR_MAX = 255 - COLOR_MIN;
public static final int BORDER_WIDTH = 5;
public static final int COL_COUNT = 10;
public static final int VISIBLE_ROW_COUNT = 20;
public static final int HIDDEN_ROW_COUNT = 2;
public static final int ROW_COUNT = VISIBLE_ROW_COUNT + HIDDEN_ROW_COUNT;
public static final int TILE_SIZE = 24;
public static final int SHADE_WIDTH = 4;
private static final int CENTER_X = COL_COUNT * TILE_SIZE / 2;
public static final int CENTER_Y = VISIBLE_ROW_COUNT * TILE_SIZE / 2;
public static final int PANEL_WIDTH = COL_COUNT * TILE_SIZE + BORDER_WIDTH * 2;
public static final int PANEL_HEIGHT = VISIBLE_ROW_COUNT * TILE_SIZE + BORDER_WIDTH * 2;
public static final Font LARGE_FONT = new Font("Tahoma", Font.BOLD, 16);
public static final Font SMALL_FONT = new Font("Tahoma", Font.BOLD, 12);
public static final long FRAME_TIME = 20L;
public static final int TYPE_COUNT = TileType.values().length;
public boolean isPaused;
public boolean isNewGame;
public boolean isGameOver;
public int level;
public int score;
public Random random;
public Clock logicTimer;
public TileType currentType;
public TileType nextType;
public int currentCol;
private int currentRow;
public int currentRotation;
public int dropCooldown;
public float gameSpeed;
public String difficulty = "Easy";
public int newLevel;
public int lines;
public int cleared;
public TileType[][] tiles;
public TetrisBoard() {
addKeyListener(this);
this.tiles = new TileType[ROW_COUNT][COL_COUNT];
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
setBackground(Color.BLACK);
startGame();
}
public void clear() {
for(int i = 0; i < ROW_COUNT; i++) {
for(int j = 0; j < COL_COUNT; j++) {
tiles[i][j] = null;
}
}
}
public boolean isValidAndEmpty(TileType type, int x, int y, int rotation) {
if(x < -type.getLeftInset(rotation) || x + type.getDimension() - type.getRightInset(rotation) >= COL_COUNT) {
return false;
}
if(y < -type.getTopInset(rotation) || y + type.getDimension() - type.getBottomInset(rotation) >= ROW_COUNT) {
return false;
}
for(int col = 0; col < type.getDimension(); col++) {
for(int row = 0; row < type.getDimension(); row++) {
if(type.isTile(col, row, rotation) && isOccupied(x + col, y + row)) {
return false;
}
}
}
return true;
}
public void addPiece(TileType type, int x, int y, int rotation) {
for(int col = 0; col < type.getDimension(); col++) {
for(int row = 0; row < type.getDimension(); row++) {
if(type.isTile(col, row, rotation)) {
setTile(col + x, row + y, type);
}
}
}
}
public int checkLines() {
int completedLines = 0;
for(int row = 0; row < ROW_COUNT; row++) {
if(checkLine(row)) {
completedLines++;
}
}
return completedLines;
}
public boolean checkLine(int line) {
for(int col = 0; col < COL_COUNT; col++) {
if(!isOccupied(col, line)) {
return false;
}
}
for(int row = line - 1; row >= 0; row--) {
for(int col = 0; col < COL_COUNT; col++) {
setTile(col, row + 1, getTile(col, row));
}
}
return true;
}
public boolean isOccupied(int x, int y) {
return tiles[y][x] != null;
}
public void setTile(int x, int y, TileType type) {
tiles[y][x] = type;
}
public TileType getTile(int x, int y) {
return tiles[y][x];
}
//#Override
public void paintComponent(Graphics g) {
this.paintComponent(g);
g.translate(BORDER_WIDTH, BORDER_WIDTH);
if(isPaused()) {
g.setFont(LARGE_FONT);
g.setColor(Color.GREEN);
String msg = "PAUSED";
g.drawString(msg, CENTER_X - g.getFontMetrics().stringWidth(msg) / 2, CENTER_Y);
} else if(isNewGame() || isGameOver()) {
g.setFont(LARGE_FONT);
g.setColor(Color.WHITE);
g.setColor(Color.GREEN);
String msg = isNewGame() ? "TETRIS" : "GAME OVER";
g.drawString(msg, CENTER_X - g.getFontMetrics().stringWidth(msg) / 2, 150);
g.setFont(SMALL_FONT);
msg = "Press Enter to Play" + (isNewGame() ? "" : " Again");
g.drawString(msg, CENTER_X - g.getFontMetrics().stringWidth(msg) / 2, 300);
} else {
for(int x = 0; x < COL_COUNT; x++) {
for(int y = HIDDEN_ROW_COUNT; y < ROW_COUNT; y++) {
TileType tile = getTile(x, y);
if(tile != null) {
drawTile(tile, x * TILE_SIZE, (y - HIDDEN_ROW_COUNT) * TILE_SIZE, g);
}
}
}
TileType type = getPieceType();
int pieceCol = getPieceCol();
int pieceRow = getPieceRow();
int rotation = getPieceRotation();
for(int col = 0; col < type.getDimension(); col++) {
for(int row = 0; row < type.getDimension(); row++) {
if(pieceRow + row >= 2 && type.isTile(col, row, rotation)) {
drawTile(type, (pieceCol + col) * TILE_SIZE, (pieceRow + row - HIDDEN_ROW_COUNT) * TILE_SIZE, g);
}
}
}
g.setColor(Color.DARK_GRAY);
for(int x = 0; x < COL_COUNT; x++) {
for(int y = 0; y < VISIBLE_ROW_COUNT; y++) {
g.drawLine(0, y * TILE_SIZE, COL_COUNT * TILE_SIZE, y * TILE_SIZE);
g.drawLine(x * TILE_SIZE, 0, x * TILE_SIZE, VISIBLE_ROW_COUNT * TILE_SIZE);
}
}
}
g.setColor(Color.GREEN);
g.drawRect(0, 0, TILE_SIZE * COL_COUNT, TILE_SIZE * VISIBLE_ROW_COUNT);
}
public void drawTile(TileType type, int x, int y, Graphics g) {
drawTile(type.getBaseColor(), type.getLightColor(), type.getDarkColor(), x, y, g);
}
public void drawTile(Color base, Color light, Color dark, int x, int y, Graphics g) {
g.setColor(base);
g.fillRect(x, y, TILE_SIZE, TILE_SIZE);
g.setColor(dark);
g.fillRect(x, y + TILE_SIZE - SHADE_WIDTH, TILE_SIZE, SHADE_WIDTH);
g.fillRect(x + TILE_SIZE - SHADE_WIDTH, y, SHADE_WIDTH, TILE_SIZE);
g.setColor(light);
for(int i = 0; i < SHADE_WIDTH; i++) {
g.drawLine(x, y + i, x + TILE_SIZE - i - 1, y + i);
g.drawLine(x + i, y, x + i, y + TILE_SIZE - i - 1);
}
}
public void startGame() {
this.random = new Random();
this.isNewGame = true;
if(this.difficulty.equals("Easy")) {
this.gameSpeed=1.0f;
}else if(this.difficulty.equals("Intermediate")) {
this.gameSpeed=3.0f;
}else if(this.difficulty.equals("Hard")) {
this.gameSpeed=6.0f;
}
this.level=1;
this.cleared=0;
this.newLevel=0;
this.logicTimer = new Clock(gameSpeed);
logicTimer.setPaused(true);
while(true) {
long start = System.nanoTime();
logicTimer.update();
if(logicTimer.hasElapsedCycle()) {
updateGame();
}
//Decrement the drop cool down if necessary.
if(dropCooldown > 0) {
dropCooldown--;
}
renderGame();
long delta = (System.nanoTime() - start) / 1000000L; // delta in miliseconds
if(delta < FRAME_TIME) {
try {
Thread.sleep(FRAME_TIME - delta); // sleeps the difference between the fps and the time for the game to process (delta)
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
public void updateGame() {
if(isValidAndEmpty(currentType, currentCol, currentRow + 1, currentRotation)) {
currentRow++;
} else {
addPiece(currentType, currentCol, currentRow, currentRotation);
cleared = checkLines();
if(cleared > 0) {
lines += cleared;
score += 50 << cleared; // left bit shift - add the number of zeros on the right to the binary version of the number on the right
// score = score + 50 << cleared;
}
//newLevel+=cleared;
gameSpeed += 0.035f;
logicTimer.setCyclesPerSecond(gameSpeed);
logicTimer.reset();
dropCooldown = 25;
if(newLevel<10) {
newLevel+=cleared;
}else if(newLevel>=10) {
level+=1;
newLevel=0;
cleared=0;
}
spawnPiece();
}
}
public void renderGame() {
repaint();
}
public void resetGame() {
this.level = 1;
this.score = 0;
this.lines = 0;
this.newLevel = 0;
this.cleared = 0;
if(this.difficulty.equals("Easy")) {
this.gameSpeed=1.0f;
}else if(this.difficulty.equals("Intermediate")) {
this.gameSpeed=3.0f;
}else if(this.difficulty.equals("Hard")) {
this.gameSpeed=6.0f;
}
this.nextType = TileType.values()[random.nextInt(TYPE_COUNT)];
this.isNewGame = false;
this.isGameOver = false;
clear();
logicTimer.reset();
logicTimer.setCyclesPerSecond(gameSpeed);
spawnPiece();
}
public void spawnPiece() {
this.currentType = nextType;
this.currentCol = currentType.getSpawnColumn();
this.currentRow = currentType.getSpawnRow();
this.currentRotation = 0;
this.nextType = TileType.values()[random.nextInt(TYPE_COUNT)];
if(!isValidAndEmpty(currentType, currentCol, currentRow, currentRotation)) {
lose();
}
}
public void lose()
{
this.isGameOver = true;
logicTimer.setPaused(isPaused);
String info = "";
if (score>HighScore.getHighScores()[9].getScore())
{
info="You got a high score!\n<br>Please enter you name.\n<br>(Note: Only 10 characters will be saved)";
JLabel label = new JLabel("<html><center>GAME OVER\n<br>" + info);
label.setHorizontalAlignment(SwingConstants.CENTER);
String name=JOptionPane.showInputDialog(null, label,"Tetris", JOptionPane.INFORMATION_MESSAGE);
if (name!=null) {
HighScore.addHighScore(new HighScore(score,level,lines,(name.length()>10)?name.substring(0, 10):name,(difficulty.length()>12)?difficulty.substring(0, 12):difficulty));
}
}else {
info="You didn't get a high score:( \n<br>Keep trying you will get it next time!";
JLabel label = new JLabel("<html><center>GAME OVER\n<br>" + info);
label.setHorizontalAlignment(SwingConstants.CENTER);
JOptionPane.showMessageDialog(null, label, "Tetris", JOptionPane.PLAIN_MESSAGE);
}
if (JOptionPane.showConfirmDialog(null, "Do you want to play again?",
"Tetris", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION) {
this.score=0;
this.level=0;
this.lines=0;
startGame();
}else
{
//If not, quit
System.exit(0);
}
}
public void rotatePiece(int newRotation) {
int newColumn = currentCol;
int newRow = currentRow;
int left = currentType.getLeftInset(newRotation);
int right = currentType.getRightInset(newRotation);
int top = currentType.getTopInset(newRotation);
int bottom = currentType.getBottomInset(newRotation);
if(currentCol < -left) {
newColumn -= currentCol - left;
} else if(currentCol + currentType.getDimension() - right >= COL_COUNT) {
newColumn -= (currentCol + currentType.getDimension() - right) - COL_COUNT + 1;
}
if(currentRow < -top) {
newRow -= currentRow - top;
} else if(currentRow + currentType.getDimension() - bottom >= ROW_COUNT) {
newRow -= (currentRow + currentType.getDimension() - bottom) - ROW_COUNT + 1;
}
if(isValidAndEmpty(currentType, newColumn, newRow, newRotation)) {
currentRotation = newRotation;
currentRow = newRow;
currentCol = newColumn;
}
}
public boolean isPaused() {
return isPaused;
}
public boolean isGameOver() {
return isGameOver;
}
public boolean isNewGame() {
return isNewGame;
}
public int getScore() {
return score;
}
public int getLevel() {
return level;
}
public String getDiff(){
return difficulty;
}
public int getLines() {
return lines;
}
public TileType getPieceType() {
return currentType;
}
public TileType getNextPieceType() {
return nextType;
}
public int getPieceCol() {
return currentCol;
}
public int getPieceRow() {
return currentRow;
}
public int getPieceRotation() {
return currentRotation;
}
//#Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()) {
case KeyEvent.VK_DOWN:
if(!isPaused && dropCooldown == 0) {
logicTimer.setCyclesPerSecond(25.0f);
}
break;
case KeyEvent.VK_LEFT:
if(!isPaused && isValidAndEmpty(currentType, currentCol - 1, currentRow, currentRotation)) {
currentCol--;
}
break;
case KeyEvent.VK_RIGHT:
if(!isPaused && isValidAndEmpty(currentType, currentCol + 1, currentRow, currentRotation)) {
currentCol++;
}
break;
case KeyEvent.VK_C:
if(!isPaused) {
rotatePiece((currentRotation == 0) ? 3 : currentRotation - 1);
}
break;
case KeyEvent.VK_V:
if(!isPaused) {
rotatePiece((currentRotation == 3) ? 0 : currentRotation + 1);
}
break;
case KeyEvent.VK_P:
if(!isGameOver && !isNewGame) {
isPaused = !isPaused;
logicTimer.setPaused(isPaused);
}
break;
case KeyEvent.VK_ENTER:
if(isGameOver || isNewGame) {
resetGame();
}
break;
case KeyEvent.VK_ESCAPE:
int ans = JOptionPane.showConfirmDialog(null, "Are you sure you want to quit?\n", "Tetris", JOptionPane.INFORMATION_MESSAGE);
if(ans==1 || ans==2) {
//isPaused = !isPaused;
//logicTimer.setPaused(isPaused);
return;
}else if(ans==0) {
System.exit(0);
}
}
}
}
//#Override
public void keyReleased(KeyEvent e) {
switch(e.getKeyCode()) {
case KeyEvent.VK_S:
logicTimer.setCyclesPerSecond(gameSpeed);
logicTimer.reset();
break;
}
}
#Override
public void keyTyped(KeyEvent e) {}
}
First of all, dont add a JMenuBar by frame.add(myJMenuBar,someConstraints). Do it by calling the method frame.setJMenuBar(myJMenuBar);
Secondly, you add the components into the frame (its content pane), and after that you frame.setLayout(new BorderLayout());, while you should first set the layout and AFTER add the components to it:
frame.setLayout(new BorderLayout());
frame.add(janela, BorderLayout.CENTER);
This works:
You might notice it replaces the ridiculously long & complicated TetrisBoard with a red panel with a preferred size of 400 x 200. This is how you should figure such things out. Post a minimal reproducible example in future.
import java.awt.*;
import javax.swing.*;
public class Tetris extends JFrame {
public Tetris() {
JFrame frame = new JFrame();
JPanel janela = new JPanel();
janela.setBackground(Color.RED);
janela.setPreferredSize(new Dimension(400,200));
JMenuBar menubar = new JMenuBar();
JMenu start = new JMenu("Start");
JMenuItem play = new JMenuItem("Play");
start.add(play);
JMenuItem exit = new JMenuItem("Exit");
start.add(exit);
JMenu help = new JMenu("Help");
JMenuItem manual = new JMenuItem("User Manual");
help.add(manual);
JMenuItem about = new JMenuItem("About");
help.add(about);
menubar.add(start);
menubar.add(help);
frame.add(janela, BorderLayout.CENTER);
frame.setJMenuBar(menubar);
janela.setFocusable(true);
frame.setTitle("Tetris");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
new Tetris();
}
}
My friend code this brick breaker game rip off, but he keep getting this error
"Error: The method getImg() is undefined for the type javax.swing.ImageIcon" and I don't know how to fix it. Can someone help?
here the code:
public class Ball extends Sprite implements iFace{
private int xDir;
private int yDir;
public Ball(){
xDir = 1;
yDir = -1;
String path = "MAJOR_JAVA_ASSESSMENT/ball.png";
ImageIcon i = new ImageIcon(this.getClass().getResource(path));
img = i.getImg();
iWidth = img.getWidth(null);
iHeight = img.getHeight(null);
reset(); }
public void move()
{
x += xDir;
y += yDir;
if(x == 0)
{
setXDir(1);
}
if(x == width - iWidth)
{
setXDir(-1);
}
if (y == 0)
{
setYDir(1);
} }
private void reset()
{
x = initBallX;
y = initBallY;
}
public void setXDir(int x)
{
xDir = x;
}
public void setYDir(int y)
{
yDir = y;
}
public int getYDir()
{
return yDir;
}
}
here another code for you:
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JPanel;
public class Board extends JPanel implements iFace
{
private Timer timer;
private String message = "You lose";
private Ball ball;
private Paddle paddle;
private Brick bricks[];
private boolean inGame = true;
public Board()
{
initBoard();
}
private void initBoard()
{
addKeyListener(new timeAdapter());
setFocusable(true);
bricks = new Brick[numOfBricks];
setDoubleBuffered(true);
timer = new Timer();
timer.scheduleAtFixedRate(new ScheduleTask(), delay, period);
}
#Override
public void addNotify()
{
super.addNotify();
gameInit();
}
private void gameInit()
{
ball = new Ball();
paddle = new Paddle();
int k = 0;
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 6; j++)
{
bricks[k] = new Brick(j * 40 + 30, i * 10 + 50);
k++;
}
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D graphics2D = (Graphics2D) g;
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
if (inGame)
{
drawObj(graphics2D);
}
else
{
gameDone(graphics2D);
}
Toolkit.getDefaultToolkit().sync();
}
private void drawObj(Graphics graphics2D)
{
graphics2D.drawImage(ball.getImg(), ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight(), this);
graphics2D.drawImage(paddle.getImg(), paddle.getX(), paddle.getY(), paddle.getWidth(), paddle.getHeight(), this);
for (int i = 0; i < numOfBricks; i++)
{
if(!bricks[i].isDead())
{
graphics2D.drawImage(bricks[i].getImg(), bricks[i].getX(), bricks[i].getY(), bricks[i].getWidth(), bricks[i].getHeight(), this);
}
}
}
private void gameDone(Graphics2D graphics2D)
{
Font f = new Font("TimesRoman", Font.BOLD, 20);
FontMetrics fm = this.getFontMetrics(f);
graphics2D.setColor(Color.RED);
graphics2D.setFont(f);
graphics2D.drawString(message, (iFace.width - fm.stringWidth(message)) / 2, iFace.width / 2);
}
private class timeAdapter extends KeyAdapter
{
#Override
public void keyReleased(KeyEvent p)
{
keyReleased(p);
}
#Override
public void keyPressed(KeyEvent p)
{
keyPressed(p);
}
}
private class ScheduleTask extends TimerTask
{
#Override
public void run()
{
ball.move();
paddle.move();
checkCollision();
repaint();
}
}
private void stopGame()
{
inGame = false;
timer.cancel();
}
private void checkCollision()
{
if(ball.getRectangle().getMaxY() > iFace.bottom)
{
stopGame();
}
for(int i = 0, j = 0; i < numOfBricks; i++)
{
if(bricks[i].isDead())
{
j++;
}
if(j == numOfBricks)
{
message = "GG YOU WIN";
stopGame();
}
}
if ((ball.getRectangle()).intersects(paddle.getRectangle()))
{
int paddlePos = (int) paddle.getRectangle().getMinX();
int ballPos = (int) ball.getRectangle().getMinX();
int first = paddlePos + 8;
int second = paddlePos + 16;
int third = paddlePos + 24;
int fourth = paddlePos + 32;
if (ballPos < first)
{
ball.setXDir(-1);
ball.setYDir(-1);
}
if (ballPos >= first && ballPos < second)
{
ball.setXDir(-1);
ball.setYDir(-1 + ball.getYDir());
}
if (ballPos >= second && ballPos < third)
{
ball.setXDir(0);
ball.setYDir(-1);
}
if (ballPos >= third && ballPos < fourth)
{
ball.setXDir(1);
ball.setYDir(-1);
}
}
for (int i = 0; i < numOfBricks; i++)
{
if ((ball.getRectangle()).intersects(bricks[i].getRectangle()))
{
int ballLeft = (int) ball.getRectangle().getMinX();
int ballHeight = (int) ball.getRectangle().getHeight();
int ballWidth = (int) ball.getRectangle().getWidth();
int ballTop = (int) ball.getRectangle().getMinY();
Point pR = new Point(ballLeft + ballWidth + 1, ballTop);
Point pL = new Point(ballLeft - 1, ballTop);
Point pT = new Point(ballLeft, ballTop - 1);
Point pB = new Point(ballLeft, ballTop + ballHeight + 1);
if (!bricks[i].isDead())
{
if (bricks[i].getRectangle().contains(pR))
{
ball.setXDir(-1);
}
else if
(bricks[i].getRectangle().contains(pL))
{
ball.setXDir(1);
}
if (bricks[i].getRectangle().contains(pT))
{
ball.setYDir(1);
}
else if (bricks[i].getRectangle().contains(pB))
{
ball.setYDir(-1);
}
bricks[i].setDeath(true);
}
}
}
}
}
i only give you the ball code as the two codes that have problems are basically have the same problem, so if someone can fix this one then, then we can the rest.
Also give you board as it the biggest and most important code, so this code may be the problem?
Thank you for reading this, and I hope you can help my friend.
getImg does not exist in the ImageIcon API, I think you'll find that it's ImageIcon#getImage instead
Having said that, I'd recommend using the ImageIO API instead
Having an issue with a game that I built. When trying to compile the code I get the weird error that this post is titled. Any help on what it could mean. The error it is telling me to look at is where I am attempting to call the draw thread. The piece of code where it is throwing the error is -- Try Thread.sleep(time);
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
public class Stacker
extends JFrame
implements KeyListener
{
int iteration = 1;
static double time = 200.0D;
static int last = 0;
static int m = 10;
static int n = 20;
JLabel[][] b;
static int[] length = { 5, 5 };
static int layer = 19;
static int[] deltax = new int[2];
static boolean press = false;
static boolean forward = true;
static boolean start = true;
public static void main(String[] args)
{
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
new Stacker();
}
public Stacker()
{
setDefaultCloseOperation(3);
this.b = new JLabel[m][n];
setLayout(new GridLayout(n, m));
for (int y = 0; y < n; y++) {
for (int x = 0; x < m; x++)
{
this.b[x][y] = new JLabel(" ");
this.b[x][y].setBackground(Color.white);
add(this.b[x][y]);
this.b[x][y].setEnabled(true);
this.b[x][y].setOpaque(true);
this.b[x][y].setBorder(BorderFactory.createLineBorder(Color.GRAY));
this.b[x][y].setPreferredSize(new Dimension(40, 30));
}
}
setFocusable(true);
addKeyListener(this);
pack();
setVisible(true);
go();
}
public void go()
{
int tmp = 0;
Component temporaryLostComponent = null;
do
{
if (forward) {
forward();
} else {
back();
}
if (deltax[1] == 10 - length[1]) {
forward = false;
} else if (deltax[1] == 0) {
forward = true;
}
draw();
try
{
Thread.sleep(time);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} while (!
press);
if (layer > 12) {
time = 150 - (this.iteration * this.iteration * 2 - this.iteration);
} else {
time -= 2.2D;
}
this.iteration += 1;
layer -= 1;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if ((layer == -1) && (length[1] > 0))
{
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
System.exit(0);
}
if (length[1] <= 0)
{
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line " + (18 - layer) + "!");
System.exit(0);
}
last = deltax[1];
start = false;
go();
}
public int check()
{
if (start) {
return length[1];
}
if (last < deltax[1])
{
if (deltax[1] + length[1] - 1 <= last + length[0] - 1) {
return length[1];
}
return length[1] - Math.abs(deltax[1] + length[1] - (last + length[0]));
}
if (last > deltax[1]) {
return length[1] - Math.abs(deltax[1] - last);
}
return length[1];
}
public void forward()
{
deltax[0] = deltax[1];
deltax[1] += 1;
}
public void back()
{
deltax[0] = deltax[1];
deltax[1] -= 1;
}
public void draw()
{
for (int x = 0; x < length[1]; x++) {
this.b[(x + deltax[0])][layer].setBackground(Color.white);
}
for (int x = 0; x < length[1]; x++) {
this.b[(x + deltax[1])][layer].setBackground(Color.BLUE);
}
}
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == 32) {
press = true;
}
}
public void keyReleased(KeyEvent arg0) {}
public void keyTyped(KeyEvent arg0) {}
}
This is a standard Java compiler error to prevent programmers accidently using a type with less precision. In your case you're trying to pass a decimal value (double) into a method which takes an integer type (Thread.sleep(milliseconds))
double value = 1.234;
Thread.sleep(value); // Error, value passed would end up being 1, not 1.234
Thread.sleep((long)value); // OK, you've explicity made the cast from 1.234 to 1 yourself
Highly recommend you read up on Java types, e.g. the official tutorial.
I'm trying to make a match 3 game. I am trying to create some visual aid to what is actually happening by first marking the gems that need to be deleted "black", and after that letting gravity do it's job. I'm struggling to do this, I called repaint(); after I marked them "black", but it doesn't seem to work. I also tried adding in revalidate(); as suggested in another question but that doesn't seem to fix the problem either. Here's the piece of code that's troubling me.
Trouble code:
public void deletePattern(Set<Gem> gemsToDelete){
for(Gem gem : gemsToDelete)
gem.setType(7);
repaint(); //This doesn't seem to work
doGravity();
switchedBack = true;
checkPattern();
}
I want to repaint the board before doGravity() and after the enhanced for loop. Could it be that I'm not using the thread correctly in the doGravity() method?
Here's the full code:
Board.java
package Game;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedHashSet;
import java.util.Set;
public class Board extends JPanel{
final int BOARDWIDTH = 8;
final int BOARDHEIGHT = 8;
private static final Color COLORS[] = { new Color(255, 0, 0), new Color(255, 128, 0), new Color(255, 255, 0), new Color(0, 255, 0), new Color(0, 255, 255), new Color(0, 0, 255), new Color(127, 0, 255), new Color(0, 0, 0), new Color(0, 0, 0), new Color(255, 255, 255)};
boolean isAlive, isPattern, switchedBack;
boolean isFirstSelected = false;
Gem[][] gems;
int fromX, fromY, toX, toY;
public Board() {
gems = new Gem[BOARDWIDTH][BOARDHEIGHT];
addMouseListener(new MouseInputAdapter());
}
int cellWidth() { return (int) getSize().getWidth() / BOARDWIDTH; }
int cellHeight() { return (int) getSize().getHeight() / BOARDHEIGHT; }
public void start(){
isPattern = switchedBack = false;
isAlive = true;
fillBoard();
checkPattern();
switchedBack = false;
}
public void paint(Graphics g) {
super.paint(g);
for (int x = 0; x < BOARDWIDTH; x++) {
for (int y = 0; y < BOARDHEIGHT; y++)
drawCell(g, x, y, gems[x][y]);
}
}
public void fillBoard(){
for (int x = 0; x < BOARDWIDTH; x++) {
for (int y = 0; y < BOARDHEIGHT; y++)
gems[x][y] = new Gem();
}
}
public void drawCell(Graphics g, int x, int y, Gem gem) {
x = x * cellWidth();
y = y * cellHeight();
g.setColor(COLORS[gem.getType()]);
g.fillRect(x, y, x + cellWidth(), y + cellHeight());
}
class MouseInputAdapter extends MouseAdapter { #Override public void mouseClicked(MouseEvent e) { selectGems(e); } }
public void selectGems(MouseEvent e){
int x = e.getX() / cellWidth();
int y = e.getY() / cellHeight();
if(!isFirstSelected) {
fromX = x;
fromY = y;
isFirstSelected = true;
}else{
toX = x;
toY = y;
if((Math.abs(fromX - toX) == 1 ^ Math.abs(fromY - toY) == 1) & (gems[fromX][fromY].getType() != gems[toX][toY].getType())) {
switchGems();
isFirstSelected = false;
}
}
}
public void switchGems(){
int tempType = gems[fromX][fromY].getType();
gems[fromX][fromY].setType(gems[toX][toY].getType());
gems[toX][toY].setType(tempType);
checkPattern();
switchedBack = false;
repaint();
}
public void checkPattern() {
Set<Gem> gemsToDelete = new LinkedHashSet<>();
isPattern = false;
for (int x = 0; x < BOARDWIDTH; x++) {
for (int y = 0; y < BOARDHEIGHT; y++) {
if (x + 2 < BOARDWIDTH && (gems[x][y].getType() == gems[x + 1][y].getType()) && (gems[x + 1][y].getType() == gems[x + 2][y].getType())) { //Checks for 3 horizontal gems in a row
isPattern = true;
gemsToDelete.add(gems[x][y]);
gemsToDelete.add(gems[x + 1][y]);
gemsToDelete.add(gems[x + 2][y]);
}
if (y + 2 < BOARDHEIGHT && (gems[x][y].getType() == gems[x][y + 1].getType()) && (gems[x][y + 1].getType() == gems[x][y + 2].getType())) { //Check for 3 vertical gems in a row
isPattern = true;
gemsToDelete.add(gems[x][y]);
gemsToDelete.add(gems[x][y + 1]);
gemsToDelete.add(gems[x][y + 2]);
}
}
}
if(!gemsToDelete.isEmpty())
deletePattern(gemsToDelete);
if(!isPattern && !switchedBack){
switchedBack = true;
switchGems();
}
}
public void deletePattern(Set<Gem> gemsToDelete){
for(Gem gem : gemsToDelete)
gem.setType(7);
repaint(); //This doesn't seem to work
doGravity();
switchedBack = true;
checkPattern();
}
public void doGravity(){
try{
Thread.sleep(1000);
}catch(InterruptedException e){e.printStackTrace();}
for (int y = 0; y < BOARDHEIGHT; y++) {
for (int x = 0; x < BOARDWIDTH; x++) {
if(gems[x][y].getType() == 7){
for (int i = y; i >= 0; i--) {
if(i == 0)
gems[x][i].setType(gems[x][i].genType());
else
gems[x][i].setType(gems[x][i-1].getType());
}
}
}
}
}
}
Gem.java
package Game;
public class Gem {
private int type;
public Gem(){
this.type = genType();
}
public int genType(){
return (int) (Math.random() * 7);
}
public void setType(int type){
this.type = type;
}
public int getType(){
return type;
}
}
Game.java
package Game;
import javax.swing.*;
public class Game extends JFrame{
public Game(){
Board board = new Board();
getContentPane().add(board);
board.start();
setTitle("Game");
setSize(600, 600);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args){
Game game = new Game();
game.setLocationRelativeTo(null);
game.setVisible(true);
}
}
Your code is initiated via a mouse click. Code invoked from a Swing listener is executed on the Event Dispatch Thread (EDT), which is also responsible for painting the GUI
The Thread.sleep() in your doGravaity() method causes the EDT to sleep, therefore the GUI can't repaint() itself until the whole looping code is finished, at which point it will just paint the final state of your animation.
Instead of sleeping, you need to use a Swing Timer to schedule animation. So basically, in the deletePattern() method you would start the Timer to do the gravity animation. This will free up the EDT to repaint itself and when the Timer fires you would animate your components one move and then do repaint() again. When the components are finished moving you stop the timer.
Read the section from the Swing tutorial on Concurrency for more information about the EDT.
Call this.invalidate() or this.postInvalidate() which then forces a repaint.
I am working on a game that my friends invented - a little variation on tic tac toe: using a 4x4 board, one player (x) need to get 3 x's in a particular manner while the other can place one 'z' and one 'o' every turn and needs to fill the entire board. My problem is not with the rules and algoritems, but with the graphics: I don't have a lot of experience with graphics, and just can't get my board to work (even without any rules - just show up as needed).
I have a Board class that represents a board. A board has a two dimensional array of Cells. Each Cell (Cell = another class of mine) is also a JButton, and I would like that every time a button is clicked his image will change - so I decided to use ImageIcon. I also have a GameMain class to control the game and a Tools class to add two buttons - 'Exit' and 'Reset'.
If you could please help me by suggesting ways to get my board to load properly, I would appreciate that. Currently the board doesn't show up at all, and if I tweak the code a bit it shows up but the buttons won't show at all.
Here's the code: GameMain.java:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class GameMain extends JPanel {
private Turn _turn;
Board _board;
private Tools _buttons;
private boolean isOver = false;
public enum GameState {PLAYING, xWON, oWON};
private GameState _currentState;
// Name-constants for the various dimensions used for graphics drawing
public static final int CELL_SIZE = 100; // cell width and height (square)
public static final int CANVAS_WIDTH = CELL_SIZE * 4; // the drawing canvas
public static final int CANVAS_HEIGHT = CELL_SIZE * 4;
public static final int GRID_WIDTH = 8; // Grid-line's width
public static final int GRID_WIDTH_HALF = GRID_WIDTH / 2; // Grid-line's half-width
public GameMain() {
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (_currentState == GameState.PLAYING) {
updateGame();
} else {
initGame(); //game over, restart
}
repaint();
}
});
setLayout(new BorderLayout());
setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT + 30));
_board = new Board();
_buttons = new Tools();
initGame();
_buttons.SetObject(_board);
add(_board, BorderLayout.CENTER);
add(_buttons, BorderLayout.SOUTH);
}
public void initGame() {
_turn = Turn.X;
_board.init();
_currentState = GameState.PLAYING;
}
public void updateGame() {
if (_board.hasWonX()) {
_currentState = GameState.xWON;
} else if (_board.hasWonO()) {
_currentState = GameState.oWON;
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.WHITE);
_board.paint(g);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("xBlock");
frame.setSize(500, 500);
// Set the content-pane of the JFrame to an instance of main JPanel
frame.setContentPane(new GameMain());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null); // center the application window
frame.setVisible(true); // show it
}
});
}
}
Board:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Board extends JPanel implements ActionListener {
private Cell[][] _cells;
private Turn _turn;
public Board() {
setLayout(new GridLayout(4, 4));
_cells = new Cell[4][4];
_turn = Turn.X;
for (int i = 0; i < _cells.length; i++) {
for (int j = 0; j < _cells[0].length; j++) {
_cells[i][j] = new Cell(i, j);
_cells[i][j].addActionListener(this);
add(_cells[i][j]);
}
}
}
//initiate board
public void init() {
_turn = Turn.X;
for (int i = 0; i < _cells.length; i++) {
for (int j = 0; j < _cells[0].length; j++) {
_cells[i][j].setState(State.EMPTY);
}
}
}
public void fillCell(Cell c) {
if (c.getState() == State.EMPTY) {
c.setState(_turn.ordinal());
c.setEnabled(false);
c.draw();
_turn = _turn.getNext();
}
}
public void checkCellsAround(Cell c) {
State state = c.getState();
State right, left, up, down;
if (c.getJ() < 3 && c.getJ() > 0) {
right = _cells[c.getI()][c.getJ() + 1].getState();
left = _cells[c.getI()][c.getJ() - 1].getState();
} else if (c.getJ() == 0) {
right = _cells[c.getI()][c.getJ() + 1].getState();
left = State.EMPTY;
} else {
right = State.EMPTY;
left = _cells[c.getI()][c.getJ() - 1].getState();
}
if (c.getI() < 3 && c.getI() > 0) {
up = _cells[c.getI() - 1][c.getJ()].getState();
down = _cells[c.getI() + 1][c.getJ()].getState();
} else if (c.getI() == 0) {
up = State.EMPTY;
down = _cells[c.getI() + 1][c.getJ()].getState();
} else {
up = _cells[c.getI() - 1][c.getJ()].getState();
down = State.EMPTY;
}
switch (state) {
case EMPTY:
break;
case X:
if ((left == State.O && right == State.O) || (up == State.O && down == State.O) || (left == State.Z && right == State.Z) || (up == State.Z && down == State.Z)) {
c.setState(State.HOURGLASS);
}
break;
case O:
if ((left == State.X && right == State.X) || (up == State.X && down == State.X)) {
c.setState(State.EMPTY);
}
break;
case Z:
if ((left == State.X && right == State.X) || (up == State.X && down == State.X)) {
c.setState(State.HOURGLASS);
}
break;
case HOURGLASS:
break;
case SCRIBBLE:
break;
}
}
public void actionPerformed(ActionEvent E) {
Cell c = (Cell) E.getSource();
fillCell(_cells[c.getI()][c.getJ()]);
}
public boolean hasWonO() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (_cells[i][j].getState() == State.EMPTY) {
return false;
}
}
}
return true;
}
public boolean hasWonX() {
return false;
}
public void paint(Graphics g) {
g.setColor(Color.GRAY);
for (int i = 1; i < 4; i++) {
g.fillRoundRect(0, GameMain.CELL_SIZE * i - GameMain.GRID_WIDTH_HALF,
GameMain.CANVAS_WIDTH - 1, GameMain.GRID_WIDTH,
GameMain.GRID_WIDTH, GameMain.GRID_WIDTH);
}
for (int j = 1; j < 4; j++) {
g.fillRoundRect(GameMain.CELL_SIZE * j - GameMain.GRID_WIDTH_HALF, 0,
GameMain.GRID_WIDTH, GameMain.CANVAS_HEIGHT - 1,
GameMain.GRID_WIDTH, GameMain.GRID_WIDTH);
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
_cells[i][j].draw();
}
}
}
}
Cell.java:
import javax.swing.ImageIcon;
import javax.swing.JButton;
public class Cell extends JButton {
private int _i, _j;
private State _state;
ImageIcon X = new ImageIcon(this.getClass().getResource("x-icon.png"));
ImageIcon O = new ImageIcon(this.getClass().getResource("o-icon.png"));
ImageIcon Z = new ImageIcon(this.getClass().getResource("z-icon.png"));
ImageIcon Hourglass = new ImageIcon(this.getClass().getResource("hourglass-icon.png"));
ImageIcon Scribble = new ImageIcon(this.getClass().getResource("scribble-icon.png"));
public Cell() {
this.setEnabled(true);
_i = 0;
_j = 0;
_state = State.EMPTY;
}
public Cell(int i, int j) {
this.setEnabled(true);
_i = i;
_j = j;
_state = State.EMPTY;
}
public int getI() {
return _i;
}
public int getJ() {
return _j;
}
public void setState(State state) {
_state = state;
if (state == State.EMPTY) {
this.setEnabled(true);
}
}
public void setState(int index) {
_state = State.values()[index];
}
public State getState() {
return _state;
}
public void draw() {
switch (_state) {
case EMPTY:
this.setIcon(null);
break;
case X:
this.setIcon(X);
break;
case O:
this.setIcon(X);
break;
case Z:
this.setIcon(X);
break;
case HOURGLASS:
this.setIcon(X);
break;
case SCRIBBLE:
this.setIcon(X);
break;
}
}
public void highlight() {
}
}
Tools.java:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Tools extends JPanel {
private JButton _exit, _reset;
private Board _board;
Tools() {
setLayout(new FlowLayout());
_exit = new JButton("Exit");
_reset = new JButton("Reset");
_exit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.exit(0);
}
});
_reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
_board.init();
}
});
add(_exit);
add(_reset);
}
public void SetObject(Board b) {
_board = b;
}
}
State.java:
public enum State {
EMPTY, X, O, Z, HOURGLASS, SCRIBBLE;
public State getNext() {
return State.values()[(this.ordinal() + 1) % State.values().length];
}
}
Turn.java:
public enum Turn {
X, O, Z;
public Turn getNext() {
return Turn.values()[(this.ordinal() + 1) % Turn.values().length];
}
}
Thanks in advance!
So after running it, you are getting an ArrayIndexOutOfBoundsException at this line in the paint method of the Board class:
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
_cells[i][j].draw(); <==========
}
}
Not sure how your game works, but by looking at the loops previous to this one, you are accessing only up to index 3 ( for (int j = 1; j < 4; j++) { ). So if you change the loop max to 4, it gets the game up and running.
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
_cells[i][j].draw();
}
}
Learning to read exceptions and stack traces is very important. It will save you a lot of future headaches. Take some time to read What is a stack trace, and how can I use it to debug my application errors?
And like I said in my comments,
It doesn't look like you're doing anything with the paintComponent in the GameMain. You should just get rid of the paintComponent method altogether in that class. Instead of trying to call board.paint(g);, try to just call board.repaint() in the mouse listener, instead of trying to repaint the main game panel. And just set the background in the constructor of the GameMain instead of in the paintComponent method.
Also in the Board class use paintComponent rather than paint. and don't forget to call super.paintComponent in the paintComponent method
Fixing all the things above, get it work (I guess).
UPDATE
As the MadMan pointed out in the comment below, it would be better to use the _cells.length to avoid having to rely magic numbers. This way you will be sure not to access an inexistant index
for (int i = 0; i < cells.length; i++) {
for (int j = 0; j < cells[i].length; j++) {
_cells[i][j].draw();
}
}