So I have this test class, I'm very clearly setting the constraints in my Deck object, but it ends up being null. Why is it doing this? I can't figure it out
import javax.swing.JFrame;
import java.awt.*;
class DeckTester
{
public DeckTester()
{
JFrame fr = new JFrame();
fr.setDefaultCloseOperation(fr.EXIT_ON_CLOSE);
fr.setSize(640,480);
fr.setLocation(300,200);
CardTable table = new CardTable();
table.setLayout(new FlowLayout());
Stack faceupPile = new Stack(0,-.25,true);
faceupPile.setStackRules(Stack.GRAB_FROM_TOP|Stack.FACEUP);
table.add(faceupPile);
StackRuleConstraints src = new StackRuleConstraints();
src.dropPile = faceupPile;
Deck deck = new Deck();
deck.setStackRules(deck.getStackRules(),src);
table.add(new Deck());
fr.add(table);
fr.setVisible(true);
}
public static void main(String[]args)
{
new DeckTester();
}
}
Here are the Deck, Stack, and StackRuleConstraints classes. I have
System.out.println("constraints are "+(ruleConstraints!=null?"not null":"null"));
included in the mouseReleased method of variable ml and setStackRules method.
...
import java.awt.Color;
public class Deck extends Stack
{
public Deck()
{
this(false);
}
public Deck(boolean includeJokers)
{
super(0,-.25,true);
for(int rank = Card.ACE; rank <= Card.KING; rank++)
{
for(int i = 0; i < 4; i++)
{
int suit = (int)Math.pow(2,i);
add(new Card(rank,suit));
}
}
if(includeJokers)
{
add(new Card(Card.JOKER,Card.RED));
add(new Card(Card.JOKER,Card.BLACK));
}
setStackRules(GRAB_FROM_TOP|DRAW_PILE);
shuffle();
}
}
...
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Collections;
import java.util.ArrayList;
import java.awt.Container;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.RenderingHints;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
public class Stack extends Container
{
public static final int GRAB_FROM_TOP = 1;
public static final int GRAB_BY_GROUP = 2;
public static final int ASCENDING = 4;
public static final int DESCENDING = 8;
public static final int SAME_SUITS = 16;
public static final int ALTERNATING_COLORS = 32;
public static final int DRAW_PILE = 64;
public static final int RECEPTACLE = 128;
public static final int FACEDOWN = 256;
public static final int FACEUP = 512;
private int rules = 0;
protected ArrayList<Card> cards = new ArrayList<Card>();
protected boolean accessible;
private double xOffset;
private double yOffset;
private Color outlineColor;
protected static Card selectedCard = null; //BOOKMARK we have to grab cards by the group, thus this will probably need to be a Card array
protected static Card[] groupedCards = null;
private Point clickLoc;
protected static int xOrigin;
protected static int yOrigin;
protected static int mouseX;
protected static int mouseY;
protected static Point cardOrigin;
protected static boolean dragging = false;
protected static double draggedCardXOffset;
protected static double draggedCardYOffset;
private ArrayList<Card> cardsToRemove = new ArrayList<Card>();
protected StackRuleConstraints ruleConstraints = null;
private MouseAdapter ml = new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
if((rules&RECEPTACLE)>0)return;
xOrigin = e.getXOnScreen();
yOrigin = e.getYOnScreen();
for(int i = 0; i < cards.size(); i++)
{
if(cards.get(i).getLocationOnScreen().x<xOrigin
&&xOrigin<cards.get(i).getLocationOnScreen().x+50
&&cards.get(i).getLocationOnScreen().y<yOrigin
&&yOrigin<cards.get(i).getLocationOnScreen().y+70)
{
cardsToRemove.clear();
groupedCards = null;
if(i!=cards.size()-1)
{
if((rules&GRAB_FROM_TOP)>0)continue;
if((rules&GRAB_BY_GROUP)>0&&i!=cards.size()-1)
{
groupedCards = new Card[cards.size()-1-i];
for(int j = i+1; j < cards.size(); j++)
{
groupedCards[j-i-1] = cards.get(j);
cardsToRemove.add(cards.get(j));
}
}
}
if((rules&DRAW_PILE)==0&&!cards.get(i).isSelected())//if the card you clicked is facedown
{
if(i!=cards.size()-1)return; //if it's not the top card, return
else //otherwise flip it faceup
{
cards.get(i).setSelected(true);
repaint();
return;
}
}
cardOrigin = cards.get(i).getLocationOnScreen();
selectedCard = cards.get(i);
draggedCardXOffset = xOffset;
draggedCardYOffset = yOffset;
}
}
}
public void mouseDragged(MouseEvent e)
{
if(!dragging&&selectedCard!=null)
{
remove(selectedCard);
for(Card c: cardsToRemove)System.out.println("moving "+c.toString());
if(cardsToRemove.size()>0)for(int i = 0; i < cardsToRemove.size(); i++){remove(cardsToRemove.get(i));}
dragging = true;
}
mouseX = e.getXOnScreen();
mouseY = e.getYOnScreen();
getParent().repaint();
}
public void mouseReleased(MouseEvent e)
{
System.out.println("constraints are "+(ruleConstraints!=null?"not null":"null"));
if(dragging)ADDING_CARD_TO_STACK:
{
for(Component c: getParent().getComponents())
{
if(c instanceof Stack
&&e.getXOnScreen()>c.getLocationOnScreen().x
&&e.getXOnScreen()<c.getLocationOnScreen().x+c.getWidth()
&&e.getYOnScreen()>c.getLocationOnScreen().y
&&e.getYOnScreen()<c.getLocationOnScreen().y+c.getHeight()
&&selectedCard!=null
&&(((Stack)c).rules&DRAW_PILE)==0)
{
((Stack)c).add(selectedCard);
if(groupedCards!=null)for(Card card: groupedCards)((Stack)c).add(card);
break ADDING_CARD_TO_STACK;
}
}
if(dragging&&selectedCard!=null){add(selectedCard);if(groupedCards!=null)for(Card card: groupedCards)add(card);}
}
else {if((rules&DRAW_PILE)>0&&ruleConstraints!=null){System.out.println("criteria met");if(ruleConstraints.dropPile!=null){remove(selectedCard);ruleConstraints.dropPile.add(selectedCard);}}}
selectedCard = null;
groupedCards = null;
cardsToRemove.clear();
dragging = false;
getParent().repaint();
}
};
public Stack()
{
this(true);
}
public Stack(boolean accessible)
{
this(0,0,accessible);
}
public Stack(double xOffset, double yOffset, boolean accessible)
{
this(new Color(5,250,10),xOffset,yOffset,accessible);
}
public Stack(Color outlineColor, double xOffset, double yOffset, boolean accessible)
{
super();
this.xOffset = xOffset;
this.yOffset = yOffset;
this.accessible = accessible;
this.outlineColor = outlineColor;
setPreferredSize(new Dimension((int)(50+Math.max(2,Math.abs(getXOffset())*cards.size())),(int)(70+Math.max(2,.25*54)+(getYOffset()<0?0:getYOffset()*cards.size()))));
addMouseListener(ml);
addMouseMotionListener(ml);
}
public void setStackRules(int rules)
{
this.rules = rules;
}
public void setStackRules(int rules, StackRuleConstraints constraints)
{
this.rules = rules;
ruleConstraints = constraints;
System.out.println("constraints are "+(ruleConstraints!=null?"not null":"null"));
}
public int getStackRules()
{
return rules;
}
public Color getOutlineColor()
{
return outlineColor;
}
public void setOutlineColor(Color c)
{
outlineColor = c;
repaint();
}
public void setXOffset(double xOffset)
{
this.xOffset = xOffset;
}
public void setYOffset(double yOffset)
{
this.yOffset = yOffset;
}
public double getXOffset()
{
return xOffset;
}
public double getYOffset()
{
return yOffset;
}
public void shuffle()
{
Collections.shuffle(cards);
super.removeAll();
for(int i = 0; i < cards.size();i++)super.add(cards.get(i));
}
public void remove(Card c)
{
cards.remove(c);
super.remove(c);
setPreferredSize(new Dimension((int)(50+Math.max(2,Math.abs(getXOffset())*cards.size())),(int)(70+Math.max(2,.25*54)+(getYOffset()<0?0:getYOffset()*cards.size()))));
setSize(getPreferredSize());
repaint();
}
// public Card drawCard()
// {
// if(cards.size()>0)
// {
// Card c = cards.get(cards.size()-1);
// remove(c);
// cards.remove(cards.size()-1);
// return c;
// }
// else return null;
// }
public void add(Card c)
{
cards.add(c);
super.add(c);
if((rules&FACEUP)>0)c.setSelected(true);
if((rules&FACEDOWN)>0)c.setSelected(false);
setPreferredSize(new Dimension((int)(50+Math.max(2,Math.abs(getXOffset())*cards.size())),(int)(70+Math.max(2,.25*54)+(getYOffset()<0?0:getYOffset()*cards.size()))));
setSize(getPreferredSize());
repaint();
}
#Override
public void paint(Graphics gi)
{
Graphics2D g = (Graphics2D)gi;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(outlineColor);
g.fillRoundRect(0,(int)(.25*54)-2,52,72,10,10);
g.setColor(getParent().getBackground());
g.fillRoundRect(2,(int)(.25*54),48,68,5,5);
double x = 1;
double y = .25*54-1;
for(int i = 0; i < cards.size(); i++)
{
cards.get(i).setLocation((int)x,(int)y);
g.drawImage(cards.get(i).getCurrentImage(),(int)x,(int)y,null);
x+=xOffset;
y+=yOffset;
}
super.paint(g);
}
}
...
public class StackRuleConstraints
{
public Stack dropPile = null;
public StackRuleConstraints()
{
}
}
The output is
constraints are not null
constraints are null
You are creating a deck, but then adding a new one to the table:
Deck deck = new Deck();
deck.setStackRules(deck.getStackRules(),src);
table.add(new Deck());
It should be
table.add(deck);
Related
I am trying to make a snake JavaFX game. The scene is a board that consists of 25x25 images. For now, I just want to make the snake go to the right before I add userInput. The first scene is loading from Controller.java and then in Main.java, I am calling run_game() method (inside controller.java) which changes the scene every second. The issue is that the first scene shows up but the scene does not get updated. There is no error, the program just keeps running till I stop it.
Here are the classes:-
Main.java
import javafx.application.Application;
import javafx.stage.Stage;
import java.util.Timer;
import java.util.TimerTask;
public class Main extends Application {
public Controller game;
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Snake Game");
game = new Controller();
primaryStage.setScene(game.scene);
primaryStage.show();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
#Override
public void run() {
try {
game.run_game();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},0,1000);
}
public static void main(String[] args) { launch(args); }
}
Controller.java
import javafx.animation.AnimationTimer;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import java.util.TimerTask;
public class Controller {
public Snake snake;
public Board board;
public Food food;
public Movement move;
public static GridPane grid_pane;
public Scene scene;
// Creating all the necessary instances of snake game
public Controller() throws InterruptedException {
food = new Food();
grid_pane = new GridPane();
//assigning a random food position to the food apple
assign_random_position_to_food();
snake = new Snake(food);
board = new Board();
move = new Movement(snake);
board.generateBoard(food.row, food.col, snake);
Parent root = grid_pane;
scene = new Scene(root, Board.BOARDHEIGHT,Board.BOARDWIDTH);
}
public void run_game() throws InterruptedException {
snake = move.move_RIGHT();
scene.setRoot( grid_pane);
}
public void assign_board_to_grid(){
for ( int row = 0; row < board.board.length; ++row)
for( int col = 0; col < board.board[row].length; ++col )
grid_pane.add(board.board[row][col], row, col, 1, 1);
}
public void assign_random_position_to_food(){
food.RandomPosition();
}
}
Board.java
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
public class Board {
public static int BOARDHEIGHT = 500;
public static int BOARDWIDTH = 500;
public static int BLOCKHEIGHT = 20;
public static int BLOCKWIDTH = 20;
public static int TOTAL_BLOCKS_ROW = BOARDHEIGHT/ BLOCKHEIGHT;
public static int TOTAL_BLOCKS_COL = BOARDWIDTH/ BLOCKWIDTH;
public ImageView[][] board;
public Image black_square;
public Image apple;
public Image snake_dot;
public GridPane gridPane = new GridPane();
public void assign_images(){
try{
apple = new Image("images/apple.png");
snake_dot = new Image("images/dot.png");
black_square = new Image("images/black_square.png");
}catch(Exception e){
System.out.println("Error: related to the address of images");
}
}
public Board(){
assign_images();
//initializing the board
board = new ImageView[TOTAL_BLOCKS_ROW][TOTAL_BLOCKS_COL];
}
public void generateBoard(int food_x, int food_y, Snake snake_body) {
for(int row=0; row< TOTAL_BLOCKS_ROW; row++){
for(int col=0; col< TOTAL_BLOCKS_COL; col++){
if(row == food_x && col == food_y)// && check if it is not on snake body)
board[row][col] = new ImageView(apple);
else if( snake_body.is_snake_present(row,col) && col != 0)
board[row][col] = new ImageView(snake_dot);
else
board[row][col] = new ImageView(black_square);
// Setting the size of each block on the scene
board[row][col].setFitHeight(BLOCKHEIGHT);
board[row][col].setFitWidth(BLOCKWIDTH);
Controller.grid_pane.add(board[row][col], row, col, 1 ,1);
}
}
}
}
Movement.java
public class Movement {
public Snake snake;
public static int new_y_position;
public static int new_x_position;
public Movement(Snake snake){
this.snake = snake;
}
public Snake move_RIGHT(){
new_x_position = snake.getHead_x() + 1;
new_y_position = snake.getHead_y();
if(!check_if_snake_is_in_bounds(new_x_position, new_y_position))
return null;
//System.out.println(new_x_position);
snake.setHead_x(new_x_position);
Block b;
for (int i = snake.snake.size() - 1; i >= 0; --i){
b = snake.snake.get(i);
b.setX(b.getX() + 1);
snake.snake.set(i,b);
}
// System.out.println(snake);
return snake;
}
public Snake move_LEFT(){
new_x_position = snake.getHead_x() - 1;
new_y_position = snake.getHead_y();
if(!check_if_snake_is_in_bounds(new_x_position, new_y_position))
return null;
//snake is inbounds update position
snake.setHead_x(new_x_position);
Block b;
for (int i = snake.snake.size() - 1; i >= 0; --i){
b = snake.snake.get(i);
b.setX(b.getX() - 1);
snake.snake.set(i,b);
}
System.out.println(snake);
return snake;
}
public Snake move_UP(){
new_x_position = snake.getHead_x();
new_y_position = snake.getHead_y() + 1;
if(!check_if_snake_is_in_bounds(new_x_position, new_y_position))
return null;
//snake is inbounds update position
snake.setHead_y(new_y_position);
Block b;
for (int i = snake.snake.size() - 1; i >= 0; --i){
b = snake.snake.get(i);
b.setY(b.getY() + 1);
snake.snake.set(i,b);
}
System.out.println(snake);
return snake;
}
public Snake move_DOWN(){
new_x_position = snake.getHead_x();
new_y_position = snake.getHead_y() - 1;
if(!check_if_snake_is_in_bounds(new_x_position, new_y_position))
return null;
//snake is inbounds update position
snake.setHead_y(new_y_position);
Block b;
for (int i = snake.snake.size() - 1; i >= 0; --i){
b = snake.snake.get(i);
b.setY(b.getY() - 1);
snake.snake.set(i,b);
}
System.out.println(snake);
return snake;
}
public boolean check_if_snake_is_in_bounds(int x, int y){
return x >= 0 && x <= Board.TOTAL_BLOCKS_COL && y >= 0 && y <= Board.TOTAL_BLOCKS_ROW;
}
}
Snake.java
import java.util.ArrayList;
public class Snake {
public ArrayList<Block> snake = new ArrayList<>();
public int snake_size = snake.size();
int head_x, head_y;
public Snake(Food food){
// initializes a snake head location on board and stores it in a list.
do{
head_x = (int) (Math.random()*Board.TOTAL_BLOCKS_COL);
head_y = (int) (Math.random()*Board.TOTAL_BLOCKS_ROW);
}while(food.getRow() == head_x || food.getCol() == head_y);
//inserting head and a block in snake arraylist
snake.add(new Block(head_x, head_y));
snake.add(new Block(head_x - 1, head_y));
}
// Adds new body part when snake eats an apple.
public void add_snake_body() {
snake.add( retrieve_new_block_position() );
}
// gets new snake body part position.
public Block retrieve_new_block_position(){
int last_block_x = snake.get(snake_size - 1).getX();
int last_block_y = snake.get(snake_size - 1).getY();
int second_last_block_x = snake.get(snake_size - 2).getX();
int second_last_block_y = snake.get(snake_size - 2).getY();
int new_block_x;
int new_block_y;
if(second_last_block_x == last_block_x){
new_block_x = last_block_x;
new_block_y = last_block_y - 1;
}
else{
new_block_x = last_block_x - 1;
new_block_y = last_block_y;
}
return new Block(new_block_x, new_block_y);
}
//checks if a position is occupied by a snake body part.
public boolean is_snake_present(int row, int col){
int index = 0;
int snake_curr_block_x;
int snake_curr_block_y;
while(index < snake.size()){
snake_curr_block_x = snake.get(index).getX();
snake_curr_block_y = snake.get(index).getY();
if( snake_curr_block_x == row && snake_curr_block_y == col)
return true;
index++;
}
return false;
}
//GETTER & SETTER METHODS
public ArrayList<Block> getSnake() {
return snake;
}
public int getHead_x() {
return head_x;
}
public void setHead_x(int head_x) {
this.head_x = head_x;
}
public int getHead_y() {
return head_y;
}
public void setHead_y(int head_y) {
this.head_y = head_y;
}
}
Food.java
import java.util.Random;
public class Food {
int row;
int col;
public Food(){
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getCol() {
return col;
}
public void setCol(int col) {
this.col = col;
}
public void RandomPosition(){
this.row = (int) (Math.random() * Board.TOTAL_BLOCKS_ROW);
this.col = (int) (Math.random() * Board.TOTAL_BLOCKS_COL);
System.out.println(this.row+" -food- "+ this.col);
//need to check if it clashes with the snake body................
}
}
Block.java
public class Block {
private int x;
private int y;
public Block(int x, int y){
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
My program generates random numbers from 0 to 12 but if the result is 12 it would set dash as the text of JLabel, instead of the number generated.
Now, I wanted to sort my JPanel in ascending order based on the JLabel contents. In case of similarities in numbers, the black JPanels are placed on the left. It works fine except when there are dashes included, in which it doesn't sort properly. I would like to insert the JPanels containing dashes anywhere but it's not working as expected.
Screencaps from a shorter version of my program:
Pure numbers:
Dash included:
Here's the shorter version of my code (using the logic of integer sorting):
import java.awt.*;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.Comparator;
public class SortFrames extends JFrame
{
static ArrayList<JPanel> panels = new ArrayList<JPanel>();
JPanel panel = new JPanel();
JPanel sortPane = new JPanel();
int toWrite = 0;
int colorGen = 0;
int comparison = 0;
Random rand = new Random();
public SortFrames()
{
for(int i = 0; i<4;i++)
{
panels.add(new JPanel());
}
for(JPanel p: panels)
{
toWrite = rand.nextInt(13);
colorGen = rand.nextInt(2);
p.add(new JLabel());
JLabel lblToSet = (JLabel)p.getComponent(0);
if(colorGen == 0)
{
p.setBackground(Color.BLACK);
lblToSet.setForeground(Color.WHITE);
}
if(colorGen == 1)
{
p.setBackground(Color.WHITE);
lblToSet.setForeground(Color.BLACK);
}
if(toWrite != 12){lblToSet.setText("" +toWrite);}
if(toWrite == 12){lblToSet.setText("-");}
p.setPreferredSize(new Dimension(30, 30));
panel.add(p);
}
sortMethod();
for(JPanel p: panels)
{
panel.add(p);
panel.revalidate();
}
add(panel);
panel.setPreferredSize(new Dimension(300, 300));
setPreferredSize(new Dimension(300, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
pack();
setLocationRelativeTo(null);
}
public void sortMethod()
{
for(int i = 0; i<(panels.size());i++)
{
for(int j = i+1; j<(panels.size());j++)
{
JLabel one = (JLabel)(panels.get(i)).getComponent(0);
JLabel two = (JLabel)(panels.get(j)).getComponent(0);
String lblOne = one.getText();
String lblTwo = two.getText();
if(!lblOne.equals("-") && !lblTwo.equals("-"))
{
int comp1 = Integer.parseInt(lblOne);
int comp2 = Integer.parseInt(lblTwo);
JPanel pnl1 = panels.get(i);
JPanel pnl2 = panels.get(j);
if(comp1 == comp2)
{
if(pnl1.getBackground() == Color.BLACK && pnl2.getBackground() == Color.WHITE)
{
panels.set(i, pnl1);
panels.set(j, pnl2);
}
if(pnl1.getBackground() == Color.WHITE && pnl2.getBackground() == Color.BLACK)
{
panels.set(i, pnl2);
panels.set(j, pnl1);
}
}
if(comp1 != comp2)
{
if(comp1>comp2)
{
panels.set(i, pnl2);
panels.set(j, pnl1);
}
}
}
if(lblOne.equals("-") && !lblTwo.equals("-"))
{
JPanel pnl1 = panels.get(i);
panels.set(rand.nextInt(panels.size()), pnl1);
}
if(!lblOne.equals("-") && lblTwo.equals("-"))
{
JPanel pnl2 = panels.get(j);
panels.set(rand.nextInt(panels.size()), pnl2);
}
}
}
}
public static void main(String args[])
{
new SortFrames();
}
}
I also have another method, which is by using Comparator class which also creates the same problem (this sorts equal numbers based on foreground but still the same as to sort equal numbers based on background so it has no effect on the said issue).
private static class JPanelSort implements Comparator<JPanel>
{
#Override
public int compare(JPanel arg0, JPanel arg1)
{
JLabel one = ((JLabel) arg0.getComponent(0));
JLabel two = ((JLabel) arg1.getComponent(0));
String firstContent = one.getText();
String secondContent = two.getText();
try
{
comparisonRes = Integer.compare(Integer.parseInt(firstContent), Integer.parseInt(secondContent));
if(comparisonRes == 0)
{
if(one.getForeground() == Color.BLACK && two.getForeground() == Color.WHITE)
{
comparisonRes = 1;
}
if(two.getForeground() == Color.BLACK && one.getForeground() == Color.WHITE)
{
comparisonRes = -1;
}
}
}
catch(NumberFormatException e)
{
comparisonRes = 0;
}
return comparisonRes;
}
}
Please tell me your ideas. Thank you.
It's much easier to sort data than to sort JPanels.
Here's mu GUI displaying your numbers.
So, lets create a Java object to hold the card data.
public class DataModel {
private final int number;
private final int colorNumber;
private final Color backgroundColor;
private final Color foregroundColor;
public DataModel(int number, int colorNumber, Color backgroundColor,
Color foregroundColor) {
this.number = number;
this.colorNumber = colorNumber;
this.backgroundColor = backgroundColor;
this.foregroundColor = foregroundColor;
}
public int getNumber() {
return number;
}
public int getColorNumber() {
return colorNumber;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public Color getForegroundColor() {
return foregroundColor;
}
}
Pretty straightforward. We have fields to hold the information and getters to retrieve the information. We can make all the fields final since we're not changing anything once we set the values.
The sort class is pretty simple as well.
public class DataModelComparator implements Comparator<DataModel> {
#Override
public int compare(DataModel o1, DataModel o2) {
if (o1.getNumber() < o2.getNumber()) {
return -1;
} else if (o1.getNumber() > o2.getNumber()) {
return 1;
} else {
if (o1.getColorNumber() < o2.getColorNumber()) {
return -1;
} else if (o1.getColorNumber() > o2.getColorNumber()) {
return 1;
} else {
return 0;
}
}
}
}
Since we keep the color number, sorting by color is as easy as sorting a number.
Now that we've moved the data to it's own List, we can concentrate on creating the GUI.
package com.ggl.testing;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SortFrames implements Runnable {
private List<DataModel> dataModels;
private JPanel[] panels;
private JLabel[] labels;
private Random random = new Random();
public SortFrames() {
this.dataModels = new ArrayList<>();
this.random = new Random();
for (int i = 0; i < 4; i++) {
int number = random.nextInt(13);
int colorNumber = random.nextInt(2);
Color backgroundColor = Color.BLACK;
Color foregroundColor = Color.WHITE;
if (colorNumber == 1) {
backgroundColor = Color.WHITE;
foregroundColor = Color.BLACK;
}
dataModels.add(new DataModel(number, colorNumber, backgroundColor,
foregroundColor));
}
Collections.sort(dataModels, new DataModelComparator());
}
#Override
public void run() {
JFrame frame = new JFrame("Sort Frames");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
panels = new JPanel[dataModels.size()];
labels = new JLabel[dataModels.size()];
for (int i = 0; i < dataModels.size(); i++) {
DataModel dataModel = dataModels.get(i);
panels[i] = new JPanel();
panels[i].setBackground(dataModel.getBackgroundColor());
labels[i] = new JLabel(getDisplayText(dataModel));
labels[i].setBackground(dataModel.getBackgroundColor());
labels[i].setForeground(dataModel.getForegroundColor());
panels[i].add(labels[i]);
mainPanel.add(panels[i]);
}
frame.add(mainPanel);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
private String getDisplayText(DataModel dataModel) {
if (dataModel.getNumber() == 12) {
return "-";
} else {
return Integer.toString(dataModel.getNumber());
}
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new SortFrames());
}
public class DataModel {
private final int number;
private final int colorNumber;
private final Color backgroundColor;
private final Color foregroundColor;
public DataModel(int number, int colorNumber, Color backgroundColor,
Color foregroundColor) {
this.number = number;
this.colorNumber = colorNumber;
this.backgroundColor = backgroundColor;
this.foregroundColor = foregroundColor;
}
public int getNumber() {
return number;
}
public int getColorNumber() {
return colorNumber;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public Color getForegroundColor() {
return foregroundColor;
}
}
public class DataModelComparator implements Comparator<DataModel> {
#Override
public int compare(DataModel o1, DataModel o2) {
if (o1.getNumber() < o2.getNumber()) {
return -1;
} else if (o1.getNumber() > o2.getNumber()) {
return 1;
} else {
if (o1.getColorNumber() < o2.getColorNumber()) {
return -1;
} else if (o1.getColorNumber() > o2.getColorNumber()) {
return 1;
} else {
return 0;
}
}
}
}
}
The lessons to be learned here are:
Separate the data from the view.
Focus on one part of the problem at a time. Divide and conquer.
Ok so I get this null Pointer exception and neither myself nor my lecturer can figure it out. (he reckons its bad logic and I'm inclined to agree)
I'm making my own 3d object and rendering it with direct world to screenpace co-ords for x and y. This gives me exactly what I want for the excercise and I am able to refine the numbers to get smooth rendering without dropping many frames on my course computer (meaning I cant reproduce the null pointer for my lecturer) but as soon as I run it on beast machine at home the code frames appear to be going much faster, and the cube I'm rendering flashes on and off repeatedly while spitting the null pointer exception to the console(even when I manipulate the numbers to slow it all down). It never crashes though.
Here are the classes that are being used.
Viewport.java(attatched to a JFrame)
package com.my3d.graphics;
import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import com.my3d.geometry.Cube;
import com.my3d.utils.Props;
public class Viewport extends Canvas implements Runnable
{
private static final long serialVersionUID = 1L;
private static final String DEFAULT_NAME = "Viewport";
private static int viewportCount = 0;
private static final BasicStroke stroke = new BasicStroke(0.5f);
private Cube cube;
private boolean running;
private String name;
private int number;
public Viewport()
{
super();
//name the Viewport
if(viewportCount < 10)
setThisName(DEFAULT_NAME +"_0" +viewportCount);
else
setThisName(DEFAULT_NAME +"_" +viewportCount);
//set Identity
setNumber(viewportCount);
//increment the Viewport counter
viewportCount++;
cube = new Cube();
cube.printCubeVertices();
cube.rotateZ(20);
cube.printCubeVertices();
cube.rotateX(20);
cube.printCubeVertices();
running = true;
}
public Viewport(String n)
{
setThisName(n);
viewportCount++;
cube = new Cube();
//cube.printCubeVertices();
//cube.rotateZ(20);
// cube.printCubeVertices();
//cube.rotateX(20);
//cube.printCubeVertices();
running = true;
}
public void tick()
{
//cube.rotateY(0.0001);
//cube.rotateX(0.0001);
repaint();
tock();
}
private void tock()
{
tick();
}
public void setThisName(String n)
{
name = n;
}
private void setNumber(int n)
{
number = n;
}
public Cube getCube()
{
return cube;
}
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(stroke);
Vertex[] verts = cube.getVerts();
for(int i = 0; i < verts.length; i++)
{
ArrayList<Vertex> links = verts[i].getLinks();
for(int j = 0; j < links.size(); j++)
{
Line2D l = new Line2D.Double(verts[i].getPosition().x()+500,/**/verts[i].getPosition().y()+400,/*|*/links.get(j).getPosition().x()+500,/**/links.get(j).getPosition().y()+400);
g2.setColor(Color.RED);
g2.draw(l);
}
}
}
#Override
public void run()
{
int frame = 0;
while(running)
{
if(frame == 1)
{
cube.rotateY(0.0004);
cube.rotateZ(-0.0005);
cube.rotateX(-0.0003);
}
if(frame > 200000)
{
//cube.rotateY(0.0001);
//cube.rotateZ(0.000015);
repaint();
frame = 0;
}
frame++;
}
}
}
Cube.java
package com.my3d.geometry;
import java.util.ArrayList;
import com.my3d.graphics.Vector3;
import com.my3d.graphics.Vertex;
public class Cube
{
private static final String DEFAULT_NAME = "Cube";
private static final double DEFAULT_SIZE = 100;
private static int cubeCount = 0;
private Vertex[] corners;
private Vector3 position;
private Vector3 rotation;
private String name;
private int number;
public Cube()
{
if(cubeCount < 10)
setName(DEFAULT_NAME +"_0" +cubeCount);
else
setName(DEFAULT_NAME +"_" +cubeCount);
//assign Id
number = cubeCount;
//Increment cube counter
cubeCount++;
corners = new Vertex[8];
for(int i = 0; i < 8; i++)
corners[i] = new Vertex();
corners[0].setPosition(new Vector3(DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE));
corners[1].setPosition(new Vector3(-DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE));
corners[2].setPosition(new Vector3(-DEFAULT_SIZE,DEFAULT_SIZE,-DEFAULT_SIZE));
corners[3].setPosition(new Vector3(DEFAULT_SIZE,DEFAULT_SIZE,-DEFAULT_SIZE));
corners[4].setPosition(new Vector3(DEFAULT_SIZE,-DEFAULT_SIZE,DEFAULT_SIZE));
corners[5].setPosition(new Vector3(-DEFAULT_SIZE,-DEFAULT_SIZE,DEFAULT_SIZE));
corners[6].setPosition(new Vector3(-DEFAULT_SIZE,-DEFAULT_SIZE,-DEFAULT_SIZE));
corners[7].setPosition(new Vector3(DEFAULT_SIZE,-DEFAULT_SIZE,-DEFAULT_SIZE));
corners[0].addLink(corners[1]);
corners[1].addLink(corners[2]);
corners[2].addLink(corners[3]);
corners[3].addLink(corners[0]);
corners[0].addLink(corners[4]);
corners[1].addLink(corners[5]);
corners[2].addLink(corners[6]);
corners[3].addLink(corners[7]);
corners[4].addLink(corners[5]);
corners[5].addLink(corners[6]);
corners[6].addLink(corners[7]);
corners[7].addLink(corners[4]);
// corners[0].addLink(corners[5]);
// corners[1].addLink(corners[6]);
// corners[2].addLink(corners[7]);
// corners[3].addLink(corners[4]);
//
// corners[0].addLink(corners[2]);
// corners[5].addLink(corners[7]);
setPosition(new Vector3());
}
public Cube(String n)
{
setName(n);
//assign Id
number = cubeCount;
//Increment cube counter
cubeCount++;
}
////////////////////////////////////////////////////////////////
//Setters
////////////////////////////////////////////////////////////////
private void setName(String n)
{
name = n;
}
private void setPosition(Vector3 v)
{
position = v;
}
public Vertex[] getVerts()
{
return corners;
}
public void printCubeVertices()
{
for(int i = 0; i < 8; i++)
System.out.println(corners[i].getPosition().toString());
}
public void rotateZ(double degrees)
{
for(int i = 0; i < corners.length; i++)
{
double tempZ = corners[i].getPosition().z();
double newX = (corners[i].getPosition().x()-position.x())*Math.cos(degrees)-(corners[i].getPosition().y()-position.y())*Math.sin(degrees);
double newY = (corners[i].getPosition().x()-position.x())*Math.sin(degrees)+(corners[i].getPosition().y()-position.y())*Math.cos(degrees);
corners[i].setPosition(new Vector3(newX,newY,tempZ));
}
}
public void rotateX(double degrees)
{
for(int i = 0; i < corners.length; i++)
{
double tempX = corners[i].getPosition().x();
double newZ = (corners[i].getPosition().z()-position.z())*Math.cos(degrees)-(corners[i].getPosition().y()-position.y())*Math.sin(degrees);
double newY = (corners[i].getPosition().z()-position.z())*Math.sin(degrees)+(corners[i].getPosition().y()-position.y())*Math.cos(degrees);
corners[i].setPosition(new Vector3(tempX,newY,newZ));
}
}
public void rotateY(double degrees)
{
for(int i = 0; i < corners.length; i++)
{
double tempY = corners[i].getPosition().y();
double newZ = (corners[i].getPosition().z()-position.z())*Math.cos(degrees)-(corners[i].getPosition().x()-position.x())*Math.sin(degrees);
double newX = (corners[i].getPosition().z()-position.z())*Math.sin(degrees)+(corners[i].getPosition().x()-position.x())*Math.cos(degrees);
corners[i].setPosition(new Vector3(newX,tempY,newZ));
}
}
}
Vertex.java
package com.my3d.graphics;
import java.util.ArrayList;
public class Vertex
{
private Vector3 position;
private ArrayList<Vertex> linked = new ArrayList<Vertex>();
public Vertex()
{
setPosition(new Vector3(0.0,0.0,0.0));
}
public Vertex(Vector3 v)
{
setPosition(v);
}
public void setPosition(Vector3 pos)
{
position = pos;
}
public void addLink(Vertex v)
{
linked.add(v);
}
public void removeLink(Vertex v)
{
linked.remove(v);
}
/////////////////////////////////////////////////////////////////
//Getters
/////////////////////////////////////////////////////////////////
public Vector3 getPosition()
{
return position;
}
public ArrayList<Vertex> getLinks()
{
return linked;
}
}
Vector3.java
package com.my3d.graphics;
public class Vector3
{
private double[] xyz;
// public double x;
// public double y;
// public double z;
public Vector3()
{
xyz = new double []{0.0,0.0,0.0};
// x = xyz[0];
// y = xyz[1];
// z = xyz[2];
}
public Vector3(double x,double y,double z)
{
xyz = new double []{x,y,z};
// xyz[0] = x;
// xyz[1] = y;
// xyz[2] = z;
}
public Vector3(double[] array)
{
try
{
if(array.length > 3)
{
RuntimeException e = new RuntimeException("The Vector3 is too big by (" +(array.length - 3) +") elements you muppet.\nremember that a Vector3 supports arrays with a length of 3. You have (" +array.length +")");
throw e;
}
if(array.length < 3)
{
RuntimeException e = new RuntimeException("The Vector3 is too small by (" +(3 - array.length) +") elements you muppet.\nremember that a Vector3 supports arrays with a length of 3. You have (" +array.length +")");
throw e;
}
xyz[0] = array[0];
xyz[1] = array[1];
xyz[2] = array[2];
}
catch(RuntimeException e)
{
System.out.println(e);
}
}
///////////////////////////////////////////////////////////////////////////
public void x(double a)
{
xyz[0] = a;
}
public void y(double a)
{
xyz[1] = a;
}
public void z(double a)
{
xyz[2] = a;
}
public double x()
{
return xyz[0];
}
public double y()
{
return xyz[1];
}
public double z()
{
return xyz[2];
}
public void normalize()
{
double length = Math.sqrt((xyz[0] * xyz[0]) + (xyz[1] * xyz[1]) + (xyz[2] * xyz[2]));
xyz[0] = xyz[0]/length;
xyz[1] = xyz[1]/length;
xyz[2] = xyz[2]/length;
}
public String toString()
{
return "(" + xyz[0] + "," + xyz[1] + "," + xyz[2] + ")";
}
}
The best I can do is trace it to public double x(){return xyz[0];}
in the Vector3 class when the paint method is called in the viewport class but when I try and step through they are never null. I get the impression that the jvm is catching up with itself while trying to reassign new positions to the vertices during the rotation.
Edit: Stacktrace
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at com.my3d.graphics.Vector3.x(Vector3.java:70)
at com.my3d.graphics.Viewport.paint(Viewport.java:107)
at java.awt.Canvas.update(Unknown Source)
at sun.awt.RepaintArea.updateComponent(Unknown Source)
at sun.awt.RepaintArea.paint(Unknown Source)
at sun.awt.windows.WComponentPeer.handleEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Additional Info:
Manager.java
package com.my3d.core;
import java.awt.Color;
import com.my3d.graphics.Viewport;
import com.my3d.graphics.Wind;
import com.my3d.utils.Props;
public class Manager {
/**
* #param args
*/
public static void main(String[] args)
{
Props.loadPropsFromFile();
Wind wind = new Wind();
wind.setVisible(true);
Viewport view = new Viewport("Main view");
view.setPreferredSize(Props.getWindowSize());
view.setBackground(Color.LIGHT_GRAY);
wind.add(view);
view.setVisible(true);
wind.pack();
view.run();
}
}
Wind.java
package com.my3d.graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import com.my3d.utils.Props;
import javax.swing.JFrame;
public class Wind extends JFrame
{
/**
*
*/
private static final long serialVersionUID = 1L;
private boolean fullScreen = false;
//Constructor
public Wind()
{
super(Props.getDefaultTitle() +Props.getTitleSplash());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fullScreen = Props.getFullScreen();
if (fullScreen)
{
setUndecorated(true);
GraphicsDevice vc;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc= ge.getDefaultScreenDevice();
vc.setFullScreenWindow(this);
}
else
{
setBounds(0,0,Props.getWindowWidth(),Props.getWindowHeight());
setLocationRelativeTo(null);
}
}
public void setTitle(String s)
{
//if(Props.getRandomSplashes())
super.setTitle(Props.getDefaultTitle() +s);
}
public void setRandomTitle()
{
if(Props.getRandomSplashes())
Props.setTitleSplash(Props.randomizeSplash());
super.setTitle(Props.getDefaultTitle() +Props.getTitleSplash());
}
public void setFullScreen(boolean b)
{
fullScreen = b;
}
//this method wont work >>
public void refactorWindow()
{
if (fullScreen)
{
setUndecorated(true);
GraphicsDevice vc;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc= ge.getDefaultScreenDevice();
vc.setFullScreenWindow(this);
}
else
{
setSize(Props.getWindowSize());
setLocationRelativeTo(null);
}
}
}
Props.java
package com.my3d.utils;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
import javax.imageio.ImageIO;
public class Props
{
private static String gameTitle;
private static String splashTitle;
private static Dimension windowSize;
//private static int gameState;
private static boolean fullScreen;
private static boolean randomSplashes;
private static BufferedImage tileSheet;
///////////////////////////////////////////////////////////////
//Getters
///////////////////////////////////////////////////////////////
public static Dimension getWindowSize()
{
return windowSize;
}
public static int getWindowWidth()
{
return (int) windowSize.getWidth();
}
public static int getWindowHeight()
{
return (int) windowSize.getHeight();
}
public static String getTitleSplash()
{
return splashTitle;
}
public static String getDefaultTitle()
{
return gameTitle;
}
public static boolean getFullScreen()
{
return fullScreen;
}
public static boolean getRandomSplashes()
{
return randomSplashes;
}
public static BufferedImage getTileSheet()
{
return tileSheet;
}
/////////////////////////////////////////////////////////////////////
//Setters
/////////////////////////////////////////////////////////////////////
public static void setWindowSize(int width, int height)
{
windowSize = new Dimension(width,height);
}
public static void setTitleSplash(String s)
{
splashTitle = s;
}
private static void setDefaultTitle(String s)
{
gameTitle = s;
}
////////////////////////////////////////////////////////////////////
//loader
////////////////////////////////////////////////////////////////////
public static void loadPropsFromFile()
{
File file = new File("res\\config\\props.props");
try
{
Scanner input = new Scanner(file);
String lInput = input.nextLine().trim();
setWindowSize(Integer.parseInt(lInput.substring(lInput.indexOf('=')+1,lInput.indexOf(','))),Integer.parseInt(lInput.substring(lInput.indexOf(',')+1,lInput.length())));
lInput = input.nextLine().trim();
if(lInput.substring(lInput.indexOf('=')+1,lInput.length()).equals("true"))
fullScreen = true;
else
fullScreen = false;
lInput = input.nextLine().trim();
if(lInput.substring(lInput.indexOf('=')+1,lInput.length()).equals("true"))
{
lInput = input.nextLine().trim();
setDefaultTitle(lInput.substring(lInput.indexOf('=')+1,lInput.length()));
randomSplashes = true;
setTitleSplash(randomizeSplash());
}
else
{
lInput = input.nextLine().trim();
setDefaultTitle(lInput.substring(lInput.indexOf('=')+1,lInput.length()));
setTitleSplash("");
randomSplashes = false;
}
lInput = input.nextLine().trim();
try
{
tileSheet = ImageIO.read(new File(loadFromDirectory(lInput.substring(lInput.indexOf('=')+1,lInput.length()))));
}
catch (IOException e)
{
System.out.println("Tilesheet Cannot be found.");
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
}
///////////////////////////////////////////////////////////////////
//Utility
///////////////////////////////////////////////////////////////////
public static String loadFromDirectory(String key)
{
String s = "";
File sFile = new File("res\\config\\dirs.dir");
try
{
Scanner sInput = new Scanner(sFile);
while(sInput.hasNextLine())
{
String st = sInput.nextLine().trim();
if(key.equals(st.substring(1,st.length())));
s = sInput.nextLine().trim();
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
return s;
}
public static String randomizeSplash()
{
if(randomSplashes)
{
File sFile = new File("res\\config\\splash.props");
ArrayList<String> sArray = new ArrayList<String>();
try
{
Scanner sInput = new Scanner(sFile);
while(sInput.hasNextLine())
{
sArray.add(sInput.nextLine().trim());
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
Random rand = new Random();
String s = sArray.get(rand.nextInt(sArray.size()));
splashTitle = s;
return s;
}
else
return "";
}
}
props.props
windowSize =1000,800
startInFullscreen =false
randomSplashes =false
defaultTitle =3D Prototype
tileSheet =tileSheet
dirs.dir
<startGame>
res\\img\\button\\Start_game_text.png
res\\img\\button\\Start_game_active.png
res\\img\\button\\Start_game_click.png
<quitGame>
res\\img\\button\\Quit_game_text.png
res\\img\\button\\Quit_game_active.png
res\\img\\button\\Quit_game_click.png
<menuBackground>
res\\img\\Menu_background.png
<resumeGame>
res\\img\\button\\Resume_game_text.png
res\\img\\button\\Resume_game_active.png
res\\img\\button\\Resume_game_click.png
<tileSheet>
res\\img\\Final_Sub_Hunt.png
Update: mark your field xyz in Vector3 as final. My reading of the JMM says that, without a synchronizer or final, it is legal for the Event Dispatch Thread (EDT), from whence your stack trace comes, to see that non-final field as not yet initialized by the constructor when the constructor is run in a separate (your Viewport.run()) thread. See this SO question or the JMM FAQ.
The correct place to look in the JLS for this is in Section 17.5 (though you really need to read all of 17, and a lot of times, to start to grok this):
An object is considered to be completely initialized when its
constructor finishes. A thread that can only see a reference to an
object after that object has been completely initialized is guaranteed
to see the correctly initialized values for that object's final
fields.
Note that the same guarantee does not hold for non-final fields; if you want safety there, you will need to use synchronization edges of some kind (explicit synchronizers, volatile, whatever.)
Related discussion from Alex Miller, who I think putters around StackOverflow as https://stackoverflow.com/users/7671/alex-miller and might poke his head in if we say hello.) And a related SO question.
Original Answer
Looks like you have an error in your Vector3(double[]) constructor, which never initializes the double[] xyz field. Only place I could find where that field would not be initialized and yield an NPE.
I can't tell from your code how many threads you have running around. If you are creating instances of Vector3 in one thread and placing them into a collection while they are being displayed by a separate rendering thread, it is possible you are observing them in a state of partial construction. Generally things are arranged to make this unlikely (I can't remember off the top of my head what the memory model says about publishing partially-constructed objects, but it can happen if you let this slip out of the constructor; I don't see that happening here.)
JA
catch(RuntimeException e)
{
System.out.println(e);
}
This segment allows you to create a Vector3 without properly initialising the array. That's a potiential problem.
Both of the non-default constructors make no attempt to check x, y or z is null before assigning them.
I would start there, then consider unit tests.
So I've been trying to get the handle of javax.swing and am having some trouble. I am attempting to implement the '8 puzzle', where there are 8 tiles and one open spot set up in a 3x3 grid, when a tile adjacent to the open spot is clicked it trades spaces with the open spot. My structure consists of a JFrame, which contains a JPanel, and the JPanel contains the 9 tiles as JComponents, however only the JPanel renders, and the tiles are nowhere to be found. Any help with this problem would be greatly appreciated.
import javax.swing.*;
public class MainFrame{
public static void main(String[] args){
JFrame frame = new JFrame("8 Puzzle");
frame.setVisible(true);
frame.setSize(600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PieceManager pm = new PieceManager();
frame.add(pm);
}
}
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import javax.swing.*;
Second class
public class PieceManager extends JPanel{
int[] possmoves;
GameTile[] pieces;
int openSpot;
public PieceManager(){
this.setSize(600,600);
this.setBackground(new Color(255,255,255));
this.setLayout(new GridLayout(3,3));
pieces = new GameTile[9];
this.init();
this.addMouseListener(new ClickAction());
}
public void init(){
ArrayList<Integer> nums = new ArrayList<Integer>();
Random rand = new Random();
for(int i=0;i<9;i++){
nums.add(i);
}
for(int i=0,j=8;i<8;i++,j--){
int p = rand.nextInt(j);
GameTile x = new GameTile(i,nums.remove(p));
pieces[i]=x;
nums.removeAll(Collections.singleton(null));
}
GameTile z = new GameTile(8,nums.get(0));
pieces[8]=z;
possmoves = new int[4];
boolean found = false;
for(int i=0;i<9||found;i++){
if(pieces[i].getID()==0){
openSpot = pieces[i].getPos();
}
}
setOpenSpot();
paint();
}
public void paint(){
this.removeAll();
for(int i=0;i<9;i++){
this.add(pieces[i]);
pieces[i].setVisible(true);
}
}
public void setOpenSpot(){
Arrays.fill(possmoves,-1);
if(openSpot==0){
possmoves[0]=1;
possmoves[1]=3;
} else if(openSpot==1){
possmoves[0]=0;
possmoves[1]=2;
possmoves[3]=4;
} else if(openSpot==2){
possmoves[0]=1;
possmoves[1]=5;
} else if(openSpot==3){
possmoves[0]=0;
possmoves[1]=4;
possmoves[2]=6;
} else if(openSpot==4){
possmoves[0]=1;
possmoves[1]=3;
possmoves[2]=5;
possmoves[3]=7;
} else if(openSpot==5){
possmoves[0]=2;
possmoves[1]=4;
possmoves[3]=8;
} else if(openSpot==6){
possmoves[0]=3;
possmoves[1]=7;
} else if(openSpot==7){
possmoves[0]=6;
possmoves[1]=4;
possmoves[2]=8;
} else if(openSpot==8){
possmoves[0]=5;
possmoves[1]=7;
}
}
public void checkCorrect(){
}
public class ClickAction implements MouseListener{
#Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int pX=(int)Math.floor(x/200);
int pY=(int)Math.floor(y/200);
int piecepressed=(pY*3)+pX;
boolean moveable = false;
int toBeMoved = -1;
for(int i=0;i<4;i++){
if(piecepressed==possmoves[i]){
moveable=true;
toBeMoved=possmoves[i];
}
}
if(moveable){
GameTile saved=pieces[openSpot];
pieces[openSpot]=pieces[toBeMoved];
pieces[toBeMoved]=saved;
openSpot=toBeMoved;
setOpenSpot();
paint();
checkCorrect();
}
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
}
Tile Class (3rd and final)
import java.awt.*;
import javax.swing.JComponent;
public class GameTile extends JComponent{
private int id;
private int position;
public GameTile(int id, int initpos){
if(id==0){
this.id=id;
this.position=initpos;
} else{
this.id=id;
this.position = initpos;
String label = Integer.toString(id);
setSize(200,200);
setBackground(new Color(0,0,0));
Label l = new Label(label,Label.CENTER);
this.add(l);
l.setVisible(true);
}
}
public void setPos(int position){
this.position=position;
}
public int getPos(){
return position;
}
public int getID(){
return id;
}
}
Use of a JComponent is fine, but you'll probably want to set it to be opaque.
Also
Don't mix AWT (i.e., Label) with Swing components.
Don't setSize(...). Instead deal with preferredSize. Best to override getPreferredSize() and return an appropriate Dimension. I've used setPreferredSize(...) in a pinch, but at the risk of the wrath of kleopatra.
Don't override a JComponent or any of its children's (JPanel included) paint method without good reason (you don't have one). Instead override paintComponent. Edit: I see that your paint method is not a true override, so this is OK -- sorry for the misunderstanding on my part.
You almost never call paint or paintComponent directly. Instead you call repaint() and let the JVM call the painting methods for you. Edit: ditto for this as this was my misunderstanding of your code. You will want to call 'revalidate()andrepaint()` after removing and replacing components in your JPanel.
You can see that your JComponents are present and accounted for if you give them borders.
Edit 2: You will need to give your JComponents a layout if you want to have components added to them easily placed and visualized. In this example I gave mine a BorderLayout.
I've combined a bunch of classes into one file for ease with compilation.
I've indicated key changes with // !! comments.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import javax.swing.*;
public class MainFrame {
public static void main(String[] args) {
JFrame frame = new JFrame("8 Puzzle");
frame.setVisible(true);
frame.setSize(600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PieceManager pm = new PieceManager();
frame.add(pm);
}
}
class PieceManager extends JPanel {
int[] possmoves;
GameTile[] pieces;
int openSpot;
public PieceManager() {
this.setSize(600, 600);
this.setBackground(new Color(255, 255, 255));
this.setLayout(new GridLayout(3, 3));
pieces = new GameTile[9];
this.init();
this.addMouseListener(new ClickAction());
}
public void init() {
ArrayList<Integer> nums = new ArrayList<Integer>();
Random rand = new Random();
for (int i = 0; i < 9; i++) {
nums.add(i);
}
for (int i = 0, j = 8; i < 8; i++, j--) {
int p = rand.nextInt(j);
GameTile x = new GameTile(i, nums.remove(p));
pieces[i] = x;
nums.removeAll(Collections.singleton(null));
}
GameTile z = new GameTile(8, nums.get(0));
pieces[8] = z;
possmoves = new int[4];
boolean found = false;
for (int i = 0; i < 9 || found; i++) {
if (pieces[i].getID() == 0) {
openSpot = pieces[i].getPos();
}
}
setOpenSpot();
paint();
}
public void paint() {
this.removeAll();
for (int i = 0; i < 9; i++) {
this.add(pieces[i]);
pieces[i].setVisible(true);
}
revalidate(); // !!
repaint(); // !!
}
public void setOpenSpot() {
Arrays.fill(possmoves, -1);
if (openSpot == 0) {
possmoves[0] = 1;
possmoves[1] = 3;
} else if (openSpot == 1) {
possmoves[0] = 0;
possmoves[1] = 2;
possmoves[3] = 4;
} else if (openSpot == 2) {
possmoves[0] = 1;
possmoves[1] = 5;
} else if (openSpot == 3) {
possmoves[0] = 0;
possmoves[1] = 4;
possmoves[2] = 6;
} else if (openSpot == 4) {
possmoves[0] = 1;
possmoves[1] = 3;
possmoves[2] = 5;
possmoves[3] = 7;
} else if (openSpot == 5) {
possmoves[0] = 2;
possmoves[1] = 4;
possmoves[3] = 8;
} else if (openSpot == 6) {
possmoves[0] = 3;
possmoves[1] = 7;
} else if (openSpot == 7) {
possmoves[0] = 6;
possmoves[1] = 4;
possmoves[2] = 8;
} else if (openSpot == 8) {
possmoves[0] = 5;
possmoves[1] = 7;
}
}
public void checkCorrect() {
}
public class ClickAction implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int pX = (int) Math.floor(x / 200);
int pY = (int) Math.floor(y / 200);
int piecepressed = (pY * 3) + pX;
boolean moveable = false;
int toBeMoved = -1;
for (int i = 0; i < 4; i++) {
if (piecepressed == possmoves[i]) {
moveable = true;
toBeMoved = possmoves[i];
}
}
if (moveable) {
GameTile saved = pieces[openSpot];
pieces[openSpot] = pieces[toBeMoved];
pieces[toBeMoved] = saved;
openSpot = toBeMoved;
setOpenSpot();
paint();
checkCorrect();
}
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
}
class GameTile extends JComponent {
private int id;
private int position;
public GameTile(int id, int initpos) {
setBorder(BorderFactory.createTitledBorder("" + id)); // !!
setLayout(new BorderLayout()); // !! so the added JLabel will show
if (id == 0) {
this.id = id;
this.position = initpos;
} else {
this.id = id;
this.position = initpos;
String label = Integer.toString(id);
// !! setSize(200, 200);
setOpaque(true); // !!
setPreferredSize(new Dimension(200, 200)); // !!
setBackground(new Color(0, 0, 0));
// !! Label l = new Label(label, Label.CENTER);
JLabel l = new JLabel(label, SwingConstants.CENTER); // !!
this.add(l);
l.setVisible(true);
}
}
public void setPos(int position) {
this.position = position;
}
public int getPos() {
return position;
}
public int getID() {
return id;
}
}
There's probably a lot more to say, but this is all I've seen so far.
Is there a way to make certain event actions specific to left and right mouse clicks?
I'm creating a minesweeper gui, so when a square is left-clicked it will be uncovered, & when it's right-clicked it will be flagged.
I wasn't sure how to syntactically check for this & couldn't find it on the tut.
Thanks for the help!
I decided to give it a go, to try to create a simple Mine Sweeper application, one without a timer or reset (yet), but that is functional and uses both a GUI cell class and a non-GUI model class (it can't be copied and used in for intro to Java homework).
Edit 1: now has reset capability:
MineSweeper.java: holds the main method and starts the JFrame
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class MineSweeper {
private JPanel mainPanel = new JPanel();
private MineCellGrid mineCellGrid;
private JButton resetButton = new JButton("Reset");
public MineSweeper(int rows, int cols, int mineTotal) {
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
mineCellGrid = new MineCellGrid(rows, cols, mineTotal);
resetButton.setMnemonic(KeyEvent.VK_R);
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mineCellGrid.reset();
}
});
mainPanel.add(mineCellGrid);
mainPanel.add(new JSeparator());
mainPanel.add(new JPanel(){{add(resetButton);}});
}
private JPanel getMainPanel() {
return mainPanel;
}
private static void createAndShowUI() {
JFrame frame = new JFrame("MineSweeper");
//frame.getContentPane().add(new MineSweeper(20, 20, 44).getMainPanel());
frame.getContentPane().add(new MineSweeper(12, 12, 13).getMainPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
MineCellGrid.java: the class that displays the grid of mine cells and times them all together.
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class MineCellGrid extends JPanel {
private MineCellGridModel model;
private List<MineCell> mineCells = new ArrayList<MineCell>();
public MineCellGrid(final int maxRows, final int maxCols, int mineNumber) {
model = new MineCellGridModel(maxRows, maxCols, mineNumber);
setLayout(new GridLayout(maxRows, maxCols));
for (int row = 0; row < maxRows; row++) {
for (int col = 0; col < maxCols; col++) {
MineCell mineCell = new MineCell(row, col);
add(mineCell);
mineCells.add(mineCell);
model.add(mineCell.getModel(), row, col);
}
}
reset();
}
public void reset() {
model.reset();
for (MineCell mineCell : mineCells) {
mineCell.reset();
}
}
}
MineCellGridModel.java: the non-GUI model for the MineCellGrid
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JOptionPane;
public class MineCellGridModel {
private MineCellModel[][] cellModelGrid;
private List<Boolean> mineList = new ArrayList<Boolean>();
private CellModelPropertyChangeListener cellModelPropChangeListener = new CellModelPropertyChangeListener();
private int maxRows;
private int maxCols;
private int mineNumber;
private int buttonsRemaining;
public MineCellGridModel(final int maxRows, final int maxCols, int mineNumber) {
this.maxRows = maxRows;
this.maxCols = maxCols;
this.mineNumber = mineNumber;
for (int i = 0; i < maxRows * maxCols; i++) {
mineList.add((i < mineNumber) ? true : false);
}
cellModelGrid = new MineCellModel[maxRows][maxCols];
buttonsRemaining = (maxRows * maxCols) - mineNumber;
}
public void add(MineCellModel model, int row, int col) {
cellModelGrid[row][col] = model;
model.addPropertyChangeListener(cellModelPropChangeListener);
}
public void reset() {
buttonsRemaining = (maxRows * maxCols) - mineNumber;
// randomize the mine location
Collections.shuffle(mineList);
// reset the model grid and set mines
for (int r = 0; r < cellModelGrid.length; r++) {
for (int c = 0; c < cellModelGrid[r].length; c++) {
cellModelGrid[r][c].reset();
cellModelGrid[r][c].setMined(mineList.get(r
* cellModelGrid[r].length + c));
}
}
// advance value property of all neighbors of a mined cell
for (int r = 0; r < cellModelGrid.length; r++) {
for (int c = 0; c < cellModelGrid[r].length; c++) {
if (cellModelGrid[r][c].isMined()) {
int rMin = Math.max(r - 1, 0);
int cMin = Math.max(c - 1, 0);
int rMax = Math.min(r + 1, cellModelGrid.length - 1);
int cMax = Math.min(c + 1, cellModelGrid[r].length - 1);
for (int row2 = rMin; row2 <= rMax; row2++) {
for (int col2 = cMin; col2 <= cMax; col2++) {
cellModelGrid[row2][col2].incrementValue();
}
}
}
}
}
}
private class CellModelPropertyChangeListener implements
PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
MineCellModel model = (MineCellModel) evt.getSource();
int row = model.getRow();
int col = model.getCol();
if (evt.getPropertyName().equals(MineCellModel.BUTTON_PRESSED)) {
if (cellModelGrid[row][col].isMineBlown()) {
mineBlown();
} else {
buttonsRemaining--;
if (buttonsRemaining <= 0) {
JOptionPane.showMessageDialog(null, "You've Won!!!", "Congratulations", JOptionPane.PLAIN_MESSAGE);
}
if (cellModelGrid[row][col].getValue() == 0) {
zeroValuePress(row, col);
}
}
}
}
private void mineBlown() {
for (int r = 0; r < cellModelGrid.length; r++) {
for (int c = 0; c < cellModelGrid[r].length; c++) {
MineCellModel model = cellModelGrid[r][c];
if (model.isMined()) {
model.setMineBlown(true);
}
}
}
}
private void zeroValuePress(int row, int col) {
int rMin = Math.max(row - 1, 0);
int cMin = Math.max(col - 1, 0);
int rMax = Math.min(row + 1, cellModelGrid.length - 1);
int cMax = Math.min(col + 1, cellModelGrid[row].length - 1);
for (int row2 = rMin; row2 <= rMax; row2++) {
for (int col2 = cMin; col2 <= cMax; col2++) {
cellModelGrid[row2][col2].pressedAction();
}
}
}
}
}
MineCell.java: the class that I started on. Uses the model class as its non-GUI nucleus.
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
/**
* http://stackoverflow.com/questions/7006029/minesweeper-action-events
*
* #author Pete
*/
#SuppressWarnings("serial")
public class MineCell extends JPanel {
private static final String LABEL = "label";
private static final String BUTTON = "button";
private static final int PS_WIDTH = 24;
private static final int PS_HEIGHT = PS_WIDTH;
private static final float LABEL_FONT_SIZE = (float) (24 * PS_WIDTH) / 30f;
private static final float BUTTON_FONT_SIZE = (float) (14 * PS_WIDTH) / 30f;
private JButton button = new JButton();
private JLabel label = new JLabel(" ", SwingConstants.CENTER);
private CardLayout cardLayout = new CardLayout();
private MineCellModel model;
public MineCell(final boolean mined, int row, int col) {
model = new MineCellModel(mined, row, col);
model.addPropertyChangeListener(new MyPCListener());
label.setFont(label.getFont().deriveFont(Font.BOLD, LABEL_FONT_SIZE));
button.setFont(button.getFont().deriveFont(Font.PLAIN, BUTTON_FONT_SIZE));
button.setMargin(new Insets(1, 1, 1, 1));
setLayout(cardLayout);
add(button, BUTTON);
add(label, LABEL);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
pressedAction();
}
});
button.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
model.upDateButtonFlag();
}
}
});
}
public MineCell(int row, int col) {
this(false, row, col);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PS_WIDTH, PS_HEIGHT);
}
public void pressedAction() {
if (model.isFlagged()) {
return;
}
model.pressedAction();
}
public void showCard(String cardConstant) {
cardLayout.show(this, cardConstant);
}
// TODO: have this change the button's icon
public void setFlag(boolean flag) {
if (flag) {
button.setBackground(Color.yellow);
button.setForeground(Color.red);
button.setText("f");
} else {
button.setBackground(null);
button.setForeground(null);
button.setText("");
}
}
private void setMineBlown(boolean mineBlown) {
if (mineBlown) {
label.setBackground(Color.red);
label.setOpaque(true);
showCard(LABEL);
} else {
label.setBackground(null);
}
}
public MineCellModel getModel() {
return model;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
model.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
model.removePropertyChangeListener(listener);
}
private class MyPCListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
String propName = evt.getPropertyName();
if (propName.equals(MineCellModel.MINE_BLOWN)) {
setMineBlown(true);
} else if (propName.equals(MineCellModel.FLAG_CHANGE)) {
setFlag(model.isFlagged());
} else if (propName.equals(MineCellModel.BUTTON_PRESSED)) {
if (model.isMineBlown()) {
setMineBlown(true);
} else {
String labelText = (model.getValue() == 0) ? "" : String
.valueOf(model.getValue());
label.setText(labelText);
}
showCard(LABEL);
}
}
}
public void reset() {
setFlag(false);
setMineBlown(false);
showCard(BUTTON);
label.setText("");
}
}
MineCellModel.java: the non-GUI model for the mine cell
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
class MineCellModel {
public static final String FLAG_CHANGE = "Flag Change";
public static final String BUTTON_PRESSED = "Button Pressed";
public static final String MINE_BLOWN = "Mine Blown";
private int row;
private int col;
private int value = 0;
private boolean mined = false;;
private boolean flagged = false;
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(
this);
private boolean pressed = false;
private boolean mineBlown = false;
public MineCellModel(boolean mined, int row, int col) {
this.mined = mined;
this.row = row;
this.col = col;
}
public void incrementValue() {
int temp = value + 1;
setValue(temp);
}
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setMineBlown(boolean mineBlown) {
this.mineBlown = mineBlown;
PropertyChangeEvent evt = new PropertyChangeEvent(this, MINE_BLOWN, false, true);
pcSupport.firePropertyChange(evt);
}
public boolean isMineBlown() {
return mineBlown;
}
public void setMined(boolean mined) {
this.mined = mined;
}
public void setFlagged(boolean flagged) {
this.flagged = flagged;
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
public boolean isMined() {
return mined;
}
public boolean isFlagged() {
return flagged;
}
public void pressedAction() {
if (pressed) {
return;
}
pressed = true;
if (mined) {
setMineBlown(true);
}
PropertyChangeEvent evt = new PropertyChangeEvent(this, BUTTON_PRESSED,
-1, value);
pcSupport.firePropertyChange(evt);
}
public void upDateButtonFlag() {
boolean oldValue = flagged;
setFlagged(!flagged);
PropertyChangeEvent evt = new PropertyChangeEvent(this, FLAG_CHANGE,
oldValue, flagged);
pcSupport.firePropertyChange(evt);
}
public void reset() {
mined = false;
flagged = false;
pressed = false;
mineBlown = false;
value = 0;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
}
Here's the whole program combined into a single MCVE file, MineSweeper.java:
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.*;
import java.beans.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("serial")
public class MineSweeper {
private JPanel mainPanel = new JPanel();
private MineCellGrid mineCellGrid;
private JButton resetButton = new JButton("Reset");
public MineSweeper(int rows, int cols, int mineTotal) {
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
mineCellGrid = new MineCellGrid(rows, cols, mineTotal);
resetButton.setMnemonic(KeyEvent.VK_R);
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mineCellGrid.reset();
}
});
mainPanel.add(mineCellGrid);
mainPanel.add(new JSeparator());
mainPanel.add(new JPanel() {
{
add(resetButton);
}
});
}
private JPanel getMainPanel() {
return mainPanel;
}
private static void createAndShowUI() {
JFrame frame = new JFrame("MineSweeper");
// frame.getContentPane().add(new MineSweeper(20, 20,
// 44).getMainPanel());
frame.getContentPane().add(new MineSweeper(12, 12, 13).getMainPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
#SuppressWarnings("serial")
class MineCellGrid extends JPanel {
private MineCellGridModel model;
private List<MineCell> mineCells = new ArrayList<>();
public MineCellGrid(final int maxRows, final int maxCols, int mineNumber) {
model = new MineCellGridModel(maxRows, maxCols, mineNumber);
setLayout(new GridLayout(maxRows, maxCols));
for (int row = 0; row < maxRows; row++) {
for (int col = 0; col < maxCols; col++) {
MineCell mineCell = new MineCell(row, col);
add(mineCell);
mineCells.add(mineCell);
model.add(mineCell.getModel(), row, col);
}
}
reset();
}
public void reset() {
model.reset();
for (MineCell mineCell : mineCells) {
mineCell.reset();
}
}
}
class MineCellGridModel {
private MineCellModel[][] cellModelGrid;
private List<Boolean> mineList = new ArrayList<Boolean>();
private CellModelPropertyChangeListener cellModelPropChangeListener = new CellModelPropertyChangeListener();
private int maxRows;
private int maxCols;
private int mineNumber;
private int buttonsRemaining;
public MineCellGridModel(final int maxRows, final int maxCols, int mineNumber) {
this.maxRows = maxRows;
this.maxCols = maxCols;
this.mineNumber = mineNumber;
for (int i = 0; i < maxRows * maxCols; i++) {
mineList.add((i < mineNumber) ? true : false);
}
cellModelGrid = new MineCellModel[maxRows][maxCols];
buttonsRemaining = (maxRows * maxCols) - mineNumber;
}
public void add(MineCellModel model, int row, int col) {
cellModelGrid[row][col] = model;
model.addPropertyChangeListener(cellModelPropChangeListener);
}
public void reset() {
buttonsRemaining = (maxRows * maxCols) - mineNumber;
// randomize the mine location
Collections.shuffle(mineList);
// reset the model grid and set mines
for (int r = 0; r < cellModelGrid.length; r++) {
for (int c = 0; c < cellModelGrid[r].length; c++) {
cellModelGrid[r][c].reset();
cellModelGrid[r][c].setMined(mineList.get(r * cellModelGrid[r].length + c));
}
}
// advance value property of all neighbors of a mined cell
for (int r = 0; r < cellModelGrid.length; r++) {
for (int c = 0; c < cellModelGrid[r].length; c++) {
if (cellModelGrid[r][c].isMined()) {
int rMin = Math.max(r - 1, 0);
int cMin = Math.max(c - 1, 0);
int rMax = Math.min(r + 1, cellModelGrid.length - 1);
int cMax = Math.min(c + 1, cellModelGrid[r].length - 1);
for (int row2 = rMin; row2 <= rMax; row2++) {
for (int col2 = cMin; col2 <= cMax; col2++) {
cellModelGrid[row2][col2].incrementValue();
}
}
}
}
}
}
private class CellModelPropertyChangeListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
MineCellModel model = (MineCellModel) evt.getSource();
int row = model.getRow();
int col = model.getCol();
if (evt.getPropertyName().equals(MineCellModel.BUTTON_PRESSED)) {
if (cellModelGrid[row][col].isMineBlown()) {
mineBlown();
} else {
buttonsRemaining--;
if (buttonsRemaining <= 0) {
JOptionPane.showMessageDialog(null, "You've Won!!!", "Congratulations",
JOptionPane.PLAIN_MESSAGE);
}
if (cellModelGrid[row][col].getValue() == 0) {
zeroValuePress(row, col);
}
}
}
}
private void mineBlown() {
for (int r = 0; r < cellModelGrid.length; r++) {
for (int c = 0; c < cellModelGrid[r].length; c++) {
MineCellModel model = cellModelGrid[r][c];
if (model.isMined()) {
model.setMineBlown(true);
}
}
}
}
private void zeroValuePress(int row, int col) {
int rMin = Math.max(row - 1, 0);
int cMin = Math.max(col - 1, 0);
int rMax = Math.min(row + 1, cellModelGrid.length - 1);
int cMax = Math.min(col + 1, cellModelGrid[row].length - 1);
for (int row2 = rMin; row2 <= rMax; row2++) {
for (int col2 = cMin; col2 <= cMax; col2++) {
cellModelGrid[row2][col2].pressedAction();
}
}
}
}
}
#SuppressWarnings("serial")
class MineCell extends JPanel {
private static final String LABEL = "label";
private static final String BUTTON = "button";
private static final int PS_WIDTH = 24;
private static final int PS_HEIGHT = PS_WIDTH;
private static final float LABEL_FONT_SIZE = (float) (24 * PS_WIDTH) / 30f;
private static final float BUTTON_FONT_SIZE = (float) (14 * PS_WIDTH) / 30f;
private JButton button = new JButton();
private JLabel label = new JLabel(" ", SwingConstants.CENTER);
private CardLayout cardLayout = new CardLayout();
private MineCellModel model;
public MineCell(final boolean mined, int row, int col) {
model = new MineCellModel(mined, row, col);
model.addPropertyChangeListener(new MyPCListener());
label.setFont(label.getFont().deriveFont(Font.BOLD, LABEL_FONT_SIZE));
button.setFont(button.getFont().deriveFont(Font.PLAIN, BUTTON_FONT_SIZE));
button.setMargin(new Insets(1, 1, 1, 1));
setLayout(cardLayout);
add(button, BUTTON);
add(label, LABEL);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
pressedAction();
}
});
button.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
model.upDateButtonFlag();
}
}
});
}
public MineCell(int row, int col) {
this(false, row, col);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PS_WIDTH, PS_HEIGHT);
}
public void pressedAction() {
if (model.isFlagged()) {
return;
}
model.pressedAction();
}
public void showCard(String cardConstant) {
cardLayout.show(this, cardConstant);
}
// TODO: have this change the button's icon
public void setFlag(boolean flag) {
if (flag) {
button.setBackground(Color.yellow);
button.setForeground(Color.red);
button.setText("f");
} else {
button.setBackground(null);
button.setForeground(null);
button.setText("");
}
}
private void setMineBlown(boolean mineBlown) {
if (mineBlown) {
label.setBackground(Color.red);
label.setOpaque(true);
showCard(LABEL);
} else {
label.setBackground(null);
}
}
public MineCellModel getModel() {
return model;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
model.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
model.removePropertyChangeListener(listener);
}
private class MyPCListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
String propName = evt.getPropertyName();
if (propName.equals(MineCellModel.MINE_BLOWN)) {
setMineBlown(true);
} else if (propName.equals(MineCellModel.FLAG_CHANGE)) {
setFlag(model.isFlagged());
} else if (propName.equals(MineCellModel.BUTTON_PRESSED)) {
if (model.isMineBlown()) {
setMineBlown(true);
} else {
String labelText = (model.getValue() == 0) ? ""
: String.valueOf(model.getValue());
label.setText(labelText);
}
showCard(LABEL);
}
}
}
public void reset() {
setFlag(false);
setMineBlown(false);
showCard(BUTTON);
label.setText("");
}
}
class MineCellModel {
public static final String FLAG_CHANGE = "Flag Change";
public static final String BUTTON_PRESSED = "Button Pressed";
public static final String MINE_BLOWN = "Mine Blown";
private int row;
private int col;
private int value = 0;
private boolean mined = false;;
private boolean flagged = false;
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
private boolean pressed = false;
private boolean mineBlown = false;
public MineCellModel(boolean mined, int row, int col) {
this.mined = mined;
this.row = row;
this.col = col;
}
public void incrementValue() {
int temp = value + 1;
setValue(temp);
}
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setMineBlown(boolean mineBlown) {
this.mineBlown = mineBlown;
PropertyChangeEvent evt = new PropertyChangeEvent(this, MINE_BLOWN, false, true);
pcSupport.firePropertyChange(evt);
}
public boolean isMineBlown() {
return mineBlown;
}
public void setMined(boolean mined) {
this.mined = mined;
}
public void setFlagged(boolean flagged) {
this.flagged = flagged;
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
public boolean isMined() {
return mined;
}
public boolean isFlagged() {
return flagged;
}
public void pressedAction() {
if (pressed) {
return;
}
pressed = true;
if (mined) {
setMineBlown(true);
}
PropertyChangeEvent evt = new PropertyChangeEvent(this, BUTTON_PRESSED, -1, value);
pcSupport.firePropertyChange(evt);
}
public void upDateButtonFlag() {
boolean oldValue = flagged;
setFlagged(!flagged);
PropertyChangeEvent evt = new PropertyChangeEvent(this, FLAG_CHANGE, oldValue, flagged);
pcSupport.firePropertyChange(evt);
}
public void reset() {
mined = false;
flagged = false;
pressed = false;
mineBlown = false;
value = 0;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
}
If you are using swing then
Is there a way to make certain event actions specific to left and
right mouse clicks?
Implement a MouseListener no component. Then in implemented method you have a MouseEvent object which has a getButton() method which tells you which mouse is pressed.
Edit
OP has asked following question but now removed it.
Is this gui nested inside the other in an action event, when
game_lost becomes true?
You can open a JDialog for this.
You may be interested in the MouseEvent Class of java.awt.event. here