I made a simple Patience/Solitaire card game. The cards don't use images but JLabels and JPanels. The game is finished when all cards are on their suit piles.
Card movement:
- mousePressed : card -> TOP (suit pile).
- mouseDragged : card(s) -> COLUMN.
I would like some feedback on my code.
package patience;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Patience extends JFrame {
final public static Dimension CARD_SIZE = new Dimension(52, 69);
final public static int CARD_CLOSED = CARD_SIZE.height / 6, CARD_OPEN = CARD_SIZE.height / 3;
final public static String SUIT[] = {"\u2660", "\u2665", "\u2663", "\u2666"}, ACE = "A", KING = "K";
final public static Object[] RANK = {ACE, 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", KING};
private Game game = new Game();
Patience() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(new Color(-16726016));
setResizable(false);
setContentPane(game);
setSize(402, 522);
setVisible(true);
JButton newGame = new JButton("\u21bb");
newGame.setBorder(null);
newGame.setFocusable(false);
newGame.setContentAreaFilled(false);
newGame.setFont(new Font(Font.DIALOG, Font.BOLD, CARD_SIZE.height / 2));
newGame.addActionListener(e -> {
game = new Game();
setContentPane(game);
revalidate();
game.STOCK.requestFocus();
});
getLayeredPane().add(newGame).setBounds(10, 440, 40, 40);
}
public static void main(String[] args) {
new Patience().setLocationRelativeTo(null);
}
}
package patience;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.util.Arrays;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Card extends JPanel {
final private JPanel FRONT = new JPanel(new BorderLayout());
final public Color COLOR;
final public String SUIT;
final public Object RANK;
Card(String suit, Object rank) {
super(null);
COLOR = new Color[] {Color.black, Color.red}[Arrays.asList(Patience.SUIT).indexOf(suit) % 2];
SUIT = suit;
RANK = rank;
setBorder(BorderFactory.createLineBorder(Color.white, 2));
setBackground(Color.blue);
setSize(Patience.CARD_SIZE);
JLabel left = new JLabel("" + rank), right = new JLabel(suit), south = new JLabel(suit, JLabel.CENTER);
left.setFont(new Font(Font.DIALOG, Font.BOLD, 25));
left.setForeground(COLOR);
left.setLayout(new BorderLayout());
left.add(right, BorderLayout.EAST);
right.setFont(new Font(Font.DIALOG, Font.PLAIN, 22));
right.setForeground(COLOR);
south.setFont(new Font(Font.DIALOG, Font.PLAIN, 36));
south.setForeground(COLOR);
FRONT.add(left, BorderLayout.CENTER);
FRONT.add(south, BorderLayout.SOUTH);
FRONT.setBorder(BorderFactory.createLineBorder(Color.gray));
FRONT.setBackground(Color.white);
FRONT.setSize(getSize());
add(FRONT).setVisible(false);
}
public boolean isOpen() {
return FRONT.isVisible();
}
public void flip() {
FRONT.setVisible(!FRONT.isVisible());
}
}
package patience;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font;
import java.awt.event.ContainerAdapter;
import java.awt.event.ContainerEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Arrays;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class Game extends JLayeredPane {
final public JButton STOCK = new JButton();
final private JPanel OPEN = new JPanel(null), COLUMN[] = new JPanel[7];
final private JLabel[] TOP = new JLabel[Patience.SUIT.length];
Game() {
STOCK.setContentAreaFilled(false);
STOCK.setBorder(BorderFactory.createLineBorder(Color.darkGray, 2));
STOCK.setLayout(null);
STOCK.addActionListener(e -> {
if (STOCK.getComponentCount() > 0) {
((Card) OPEN.add(STOCK.getComponent(0), 0)).flip();
} else if (OPEN.getComponentCount() > 0) {
for (Component card : OPEN.getComponents()) {
((Card) STOCK.add(card, 0)).flip();
}
repaint();
}
});
add(STOCK).setBounds(10, 10, Patience.CARD_SIZE.width, Patience.CARD_SIZE.height);
for (String suit : Patience.SUIT) {
for (Object rank : Patience.RANK) {
STOCK.add(new Card(suit, rank), (int) (Math.random() * (STOCK.getComponentCount() + 1)));
}
}
OPEN.setOpaque(false);
add(OPEN).setBounds(STOCK.getX() + STOCK.getWidth() + 2, STOCK.getY(), Patience.CARD_SIZE.width, Patience.CARD_SIZE.height);
for (int i = 0; i < TOP.length; i++) {
TOP[i] = new JLabel(new String[] {"\u2664", "\u2661", "\u2667", "\u2662"}[i], JLabel.CENTER);
TOP[i].setFont(new Font(Font.DIALOG, Font.BOLD, Patience.CARD_SIZE.height / 2));
add(TOP[i]).setBounds(STOCK.getX() + (3 + i) * (OPEN.getX() - STOCK.getX()), STOCK.getY(), Patience.CARD_SIZE.width, Patience.CARD_SIZE.height);
}
for (int i = 0; i < COLUMN.length; i++) {
COLUMN[i] = new JPanel(null);
COLUMN[i].setOpaque(false);
COLUMN[i].addContainerListener(new ContainerAdapter() {
@Override
public void componentAdded(ContainerEvent e) {
if (e.getContainer().getComponentCount() > 1) {
e.getChild().setLocation(0, e.getContainer().getComponent(1).getY() + new int[] {Patience.CARD_CLOSED, Patience.CARD_OPEN}[Boolean.compare(((Card) e.getContainer().getComponent(1)).isOpen(), false)]);
}
}
});
add(COLUMN[i]).setBounds(STOCK.getX() + i * (OPEN.getX() - STOCK.getX()), STOCK.getY() + STOCK.getHeight() + STOCK.getY(), Patience.CARD_SIZE.width, i * Patience.CARD_CLOSED + 11 * Patience.CARD_OPEN + Patience.CARD_SIZE.height);
for (int row = 0; row <= i; row++) {
COLUMN[i].add(STOCK.getComponent(0), 0);
}
((Card) COLUMN[i].getComponent(0)).flip();
}
addMouseListener(new MouseAdapter() {
long time = System.currentTimeMillis();
@Override
public void mousePressed(MouseEvent e) {
try {
Container pressed = (Container) getComponentAt(e.getPoint());
if (getLayer(pressed) == 0) {
Card card = (Card) pressed.getComponentAt(pressed.getMousePosition());
if (card.isOpen()) {
JPanel dragged = new JPanel(null);
dragged.setBounds(pressed.getX(), pressed.getY() + card.getY(), pressed.getWidth(), pressed.getComponent(0).getY() + pressed.getComponent(0).getHeight() - card.getY());
for (int i = pressed.getComponentZOrder(card), y = 0; i >= 0; i--, y += Patience.CARD_OPEN) {
dragged.add(pressed.getComponent(i), 0).setLocation(0, y);
}
MouseMotionAdapter mma = new MouseMotionAdapter() {
int x = dragged.getX() - e.getX(), y = dragged.getY() - e.getY();
@Override
public void mouseDragged(MouseEvent e) {
dragged.setLocation(Math.max(0, Math.min(e.getX() + x, getWidth() - dragged.getWidth())), Math.max(0, Math.min(e.getY() + y, getHeight() - dragged.getHeight())));
}
};
add(dragged, new Integer(1));
addMouseMotionListener(mma);
addMouseListener(new MouseAdapter() {
Container next = pressed;
@Override
public void mouseReleased(MouseEvent e) {
removeMouseListener(this);
removeMouseMotionListener(mma);
remove(dragged);
Container released = (Container) getComponentAt(e.getPoint());
if (released == pressed) {
JLabel suit = TOP[Arrays.asList(Patience.SUIT).indexOf(card.SUIT)];
if (card == dragged.getComponent(0) && card.RANK == Patience.RANK[suit.getComponentCount()]) {
next = suit;
}
} else if (Arrays.asList(COLUMN).contains(released) && card.RANK != Patience.ACE) {
if (card.RANK == Patience.KING) {
if (released.getComponentCount() == 0) {
next = released;
}
} else if (released.getComponentCount() >= 1) {
Card last = (Card) released.getComponent(0);
if (last.isOpen() && last.COLOR != card.COLOR && last.RANK == Patience.RANK[Arrays.asList(Patience.RANK).indexOf(card.RANK) + 1]) {
next = released;
}
}
}
for (int i = dragged.getComponentZOrder(card); i >= 0; i--) {
next.add(dragged.getComponent(i), 0);
}
if (TOP[0].getComponentCount() == Patience.RANK.length && TOP[1].getComponentCount() == Patience.RANK.length && TOP[2].getComponentCount() == Patience.RANK.length && TOP[3].getComponentCount() == Patience.RANK.length) {
time = (System.currentTimeMillis() - time) / 1000;
JLabel completed = new JLabel((time / 60) + " min " + (time % 60) + " sec", JLabel.CENTER);
completed.setFont(new Font(Font.DIALOG, Font.BOLD, Patience.CARD_SIZE.height / 3));
completed.setSize(getSize());
add(completed, new Integer(1));
}
repaint();
}
});
} else if (card == pressed.getComponent(0)) {
card.flip();
}
}
} catch (Exception ex) { }
}
});
}
}