//------------------------------------------------------------------------------
// Memory.java 1.03
// Copyright (C) 2001, 2017 by Alexander Adam
// Tested with Java 8
// No Warranty of any kind.
//------------------------------------------------------------------------------
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import java.net.URL;
/**
* Memory
* No Warranty of any kind.
* @author Alexander Adam
Copyright (C) 2001, 2017
* @version 1.03
*/
class Memory extends JPanel {
/*
* Constants
*/
private static final long serialVersionUID = -324865921496900578L;
// String constants
private final static String sTitle = "Memory";
private final static String sAbout = sTitle + ". Version 1.03. (C) Copyright 2001, 20017 by Alexander Adam";
private final static String sNewGame = "Neues Spiel";
private final static String sCrossPF = "Cross Platform";
private final static String sSystem = "System";
private final static String sUILF = "Look & Feel";
private final static String sRows = "Zeilen";
private final static String sCols = "Spalten";
private final static String sTries = "Versuche";
private final static String sImageSet = "Bilder Schema";
private final static String sStandard = "Standard";
private final static String sArt = "Kunst";
private final static String sMenuGame = "Spiel";
private final static String sMenuOptions = "Einstellungen";
private final static String sMenuHelp = "Hilfe";
private final static String sItemGameNew = "Neu";
private final static String sItemGameExit = "Beenden";
private final static String sItemHelpAbout = "Info";
private final static String sTipItemGameNew = "Neues Spiel beginnen";
private final static String sTipItemGameExit = "Spiel beenden";
private final static String sTipMenuOptionColsRows = "Anzahl Spalten/Zeilen festlegen";
private final static String sTipMenuOptionUILF = "Generelles Aussehen";
private final static String sAcceleratorHelpAbout = "F1";
private final static char cMnemonicGame = 'S';
private final static char cMnemonicOptions = 'E';
private final static char cMnemonicHelp = 'H';
private final static char cMnemonicGameNew = 'N';
private final static char cMnemonicGameExit = 'B';
private final static char cMnemonicOptionsCR = 'Z';
private final static char cMnemonicOptionsLF = 'L';
private final static char cMnemonicHelpAbout = 'I';
// Initial application size
private final static int startSizeX = 800;
private final static int startSizeY = 600;
// Fontsizes
private final static int triesFontSize = 18;
private final static int imageSetFontSize = 10;
private final static int newGameButtonFontSize = 18;
// Number of Cards
private final static int maxCardCols = 10;
private final static int maxCardRows = 8;
private final static int maxNumCards = maxCardCols*maxCardRows;
private final static int maxNumImages = 40; // This must match with the imageSets used
/*
* Variables
*/
// New Game Button
private JButton newGameButton = new JButton(sNewGame);
// Cards
private Card[] cards = new Card[maxNumCards];
private Card[] openCards = new Card[2];
private int openCardNum = 0;
// Images
private Image[] images = new Image[maxNumImages];
private boolean[] imageUsed = new boolean[maxNumImages];
// Image Sets
private JRadioButton imageSetStd;
private JRadioButton imageSetArt;
private int imageSetNumImages = maxNumImages;
// Options
private JScrollBar cardColsScroller;
private JScrollBar cardRowsScroller;
private JLabel cardColsValueLabel;
private JLabel cardRowsValueLabel;
private int cardCols = Math.min( maxCardCols, Math.max( 6, maxCardCols/2 ) );
private int cardRows = Math.min( maxCardRows, Math.max( 4, maxCardRows/2 ) );
// Number Tries
private JLabel numTriesLabel;
private int numTries = 0;
// Number of cards use in current game
private int numCards = cardRows*cardCols;
// Initial Card Text
private String cardText = "Memory";
// Cards Grid
JPanel cardGrid = new JPanel();
// Game Panel
JPanel gamePanel = new JPanel();
/**
* Memory constructor
* sets main application layout
*/
Memory() {
super(true);
// Set up System Look&Feel
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch ( Exception ex ) {
JOptionPane.showMessageDialog( null, ex.toString() );
}
// Get Font
Font font = getFont();
// Setup Options Panel
JPanel northPanel = new JPanel();
JPanel statusPanel = new JPanel();
// numTries
JLabel triesLabel = new JLabel(sTries);
numTriesLabel = new JLabel(""+numTries);
// tries Font
if ( font != null ) {
font = new Font( font.getName(), Font.BOLD, triesFontSize );
}
if ( font != null ) {
triesLabel.setFont( font );
numTriesLabel.setFont( font );
}
statusPanel.setLayout( new FlowLayout());
statusPanel.add(triesLabel);
statusPanel.add(numTriesLabel);
northPanel.add(statusPanel);
// Setup Options Panel
JPanel southPanel = new JPanel();
southPanel.setLayout( new GridLayout(2,1) );
JPanel imageSetPanel = new JPanel();
JLabel imageSetLabel = new JLabel(sImageSet);
ButtonGroup imageSetGroup = new ButtonGroup();
imageSetStd = new JRadioButton(sStandard,true);
imageSetArt = new JRadioButton(sArt,false);
imageSetGroup.add(imageSetStd);
imageSetGroup.add(imageSetArt);
if ( font != null ) {
font = new Font( font.getName(), Font.BOLD, imageSetFontSize );
}
if ( font != null ) {
imageSetLabel.setFont( font );
imageSetStd.setFont( font );
imageSetArt.setFont( font );
}
imageSetPanel.add(imageSetLabel);
imageSetPanel.add(imageSetStd);
imageSetPanel.add(imageSetArt);
// New Game Button
if ( font != null ) {
font = new Font( font.getName(), Font.BOLD, newGameButtonFontSize );
}
if ( font != null ) {
newGameButton.setFont( font );
}
if ( (cardRows*cardCols)%2 == 0 ) {
newGameButton.setEnabled(true);
} else {
newGameButton.setEnabled(false);
}
newGameButton.addActionListener( new NewGameListener() );
southPanel.add(imageSetPanel);
southPanel.add(newGameButton);
// Game Panel
gamePanel = new JPanel();
gamePanel.setLayout(new BorderLayout());
gamePanel.add("North",northPanel);
gamePanel.add("Center",cardGrid);
gamePanel.add("South",southPanel);
// Main Layout
setLayout(new BorderLayout());
add("North", createMenubar() );
add("Center", gamePanel );
}
/*
* Main
*/
public static void main(String args[]) {
try {
JFrame frame = new JFrame();
Memory memory = new Memory();
Cursor handCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
frame.setCursor( handCursor );
frame.setTitle(sTitle);
frame.getContentPane().add( memory );
frame.addWindowListener(new MainWindowAdapter());
frame.setSize(startSizeX, startSizeY);
frame.setVisible(true);
} catch ( Exception ex ) {
JOptionPane.showMessageDialog( null, ex.toString() );
}
}
/**
* applications main WindowAdapter
*/
private static final class MainWindowAdapter extends WindowAdapter {
public void windowClosing(WindowEvent e) {
Close();
}
public void windowActivated(WindowEvent e) {
e.getWindow().repaint();
}
}
/**
* applications main close function
*/
private static void Close() {
System.exit(0);
}
/**
* createMenubar()
* create the menubar
* add Mnemonics
* add ToolTips
* add ActionListeners
* setup ComponentEnablers
*/
private JMenuBar createMenubar()
{
JMenuBar mb = new JMenuBar();
/*
* Menu Game
*/
JMenu menuGame = new JMenu(sMenuGame);
JMenuItem itemGameNew = new JMenuItem(sItemGameNew, cMnemonicGameNew);
JMenuItem itemGameExit = new JMenuItem(sItemGameExit, cMnemonicGameExit);
menuGame.setMnemonic(cMnemonicGame);
itemGameNew.setToolTipText(sTipItemGameNew);
itemGameExit.setToolTipText(sTipItemGameExit);
itemGameNew.addActionListener(new NewGameListener());
itemGameExit.addActionListener(new ExitGameListener());
menuGame.add( itemGameNew );
menuGame.addSeparator();
menuGame.add( itemGameExit );
/*
* Menu Options
*/
JMenu menuOptions = new JMenu(sMenuOptions);
JMenu menuOptionColsRows = new JMenu(sCols+"/"+sRows);
JMenu menuOptionUILF = new JMenu(sUILF);
// Option Rows/Cols
JPanel colsRowsPanel = new JPanel();
JPanel colsPanel = new JPanel();
JPanel rowsPanel = new JPanel();
colsRowsPanel.setLayout( new GridLayout(2,1,5,5) );
colsPanel.setLayout( new BorderLayout() );
rowsPanel.setLayout( new BorderLayout() );
// Rows
cardRowsScroller = new JScrollBar( JScrollBar.VERTICAL, 1, 1, 1, maxCardRows + 1 );
cardRowsScroller.setValue(cardRows);
cardRowsScroller.addAdjustmentListener(new CardRowsListener() );
cardRowsValueLabel = new JLabel( ""+cardRows+" " );
cardRowsValueLabel.setHorizontalAlignment(SwingConstants.CENTER);
rowsPanel.add("West",cardRowsScroller);
rowsPanel.add("East",cardRowsValueLabel);
// Cols
cardColsScroller = new JScrollBar( JScrollBar.HORIZONTAL, 1, 1, 1, maxCardCols + 1 );
cardColsScroller.setValue(cardCols);
cardColsScroller.addAdjustmentListener(new CardColsListener() );
cardColsValueLabel = new JLabel( ""+cardCols );
cardColsValueLabel.setHorizontalAlignment(SwingConstants.CENTER);
colsPanel.add("North",cardColsScroller);
colsPanel.add("South",cardColsValueLabel);
colsRowsPanel.add(colsPanel);
colsRowsPanel.add(rowsPanel);
menuOptionColsRows.setToolTipText(sTipMenuOptionColsRows);
menuOptionColsRows.setMnemonic(cMnemonicOptionsCR);
menuOptionColsRows.add(colsRowsPanel);
// Option UI L&F
ButtonGroup uiLFGroup = new ButtonGroup();
JRadioButtonMenuItem uiLFSystem = new JRadioButtonMenuItem(sSystem,true);
JRadioButtonMenuItem uiLFCrossPF = new JRadioButtonMenuItem(sCrossPF,false);
uiLFSystem.addActionListener(new UILFSystemListener());
uiLFCrossPF.addActionListener(new UILFCrossPFListener());
uiLFGroup.add(uiLFSystem);
uiLFGroup.add(uiLFCrossPF);
menuOptionUILF.setToolTipText(sTipMenuOptionUILF);
menuOptionUILF.setMnemonic(cMnemonicOptionsLF);
menuOptionUILF.add(uiLFSystem);
menuOptionUILF.add(uiLFCrossPF);
menuOptions.setMnemonic(cMnemonicOptions);
menuOptions.add( menuOptionColsRows );
menuOptions.add( menuOptionUILF );
/*
* Menu Help
*/
JMenu menuHelp = new JMenu(sMenuHelp);
JMenuItem itemHelpAbout = new JMenuItem(sItemHelpAbout, cMnemonicHelpAbout);
menuHelp.setMnemonic(cMnemonicHelp);
itemHelpAbout.setAccelerator(KeyStroke.getKeyStroke(sAcceleratorHelpAbout));
itemHelpAbout.addActionListener(new HelpAboutListener());
menuHelp.add( itemHelpAbout );
/*
* MenuBar
*/
mb.add( menuGame );
mb.add( menuOptions );
mb.add( menuHelp );
return mb;
}
/**
* AdjustmentListener for cardRows
*/
private class CardRowsListener implements AdjustmentListener {
public void adjustmentValueChanged(AdjustmentEvent evt) {
JScrollBar source = (JScrollBar)evt.getSource();
cardRows = source.getValue();
cardRowsValueLabel.setText( ""+cardRows+" " );
if ( (cardRows*cardCols)%2 == 0 ) {
newGameButton.setEnabled(true);
} else {
newGameButton.setEnabled(false);
}
}
}
/**
* AdjustmentListener for cardCols
*/
private class CardColsListener implements AdjustmentListener {
public void adjustmentValueChanged(AdjustmentEvent evt) {
JScrollBar source = (JScrollBar)evt.getSource();
cardCols = source.getValue();
cardColsValueLabel.setText( ""+cardCols );
if ( (cardRows*cardCols)%2 == 0 ) {
newGameButton.setEnabled(true);
} else {
newGameButton.setEnabled(false);
}
}
}
/**
* ActionListener for cards Buttons
*/
private class CardsListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
Card card = (Card)evt.getSource();
int i=0;
while ( !cards[i].equals(card) ) {
i++;
if ( i>=numCards ) {
// This would be a programming error
// JOptionPane.showMessageDialog( null, "Error: card not found" );
return;
}
}
openCardNum++;
switch ( openCardNum ) {
case 1: // First card opened
card.showFrontside(); // Show first card
numTries++; // increase numTries
numTriesLabel.setText(""+numTries); // show numTries
openCards[0] = card;
break;
case 2: // Second card opened
if ( card == openCards[0] ) { // Same as first
card.showBackside(); // Flip back
openCardNum = 0; // Reset openCardNum
} else {
card.showFrontside(); // Show second card
openCards[1] = card;
if ( openCards[0].imageNum == openCards[1].imageNum ) {
// Cards match, make them brighter
openCards[0].bg = openCards[0].bg.brighter();
openCards[1].bg = openCards[1].bg.brighter();
openCards[0].setEnabled(false);
openCards[1].setEnabled(false);
// Reset openCardNum
openCardNum = 0;
}
}
break;
default: // 'Third' -> First Card.
openCards[0].showBackside(); // Hide first ..
openCards[1].showBackside(); // and second card
openCardNum = 1; // actually it the first opened card
card.showFrontside(); // show 'first' card
numTries++; // increase numTries
numTriesLabel.setText(""+numTries); // show numTries
openCards[0] = card;
break;
}
}
}
/**
* ActionListener for Game-New
*/
private class NewGameListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
numCards = cardRows*cardCols;
if (numCards%2 == 1) {
// Incorrect number of cards, return
return;
}
cardGrid.setVisible(false);
gamePanel.remove(cardGrid);
getImageSet();
buildGrid();
gamePanel.add("Center",cardGrid);
openCardNum = 0;
numTries = 0;
cardGrid.setVisible(true);
}
}
/**
* ActionListener for Game-Exit
*/
private class ExitGameListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Close();
}
}
/**
* ActionListener for Help-About
*/
private class HelpAboutListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog( null, sAbout );
}
}
/**
* ActionListener for UI L&F System
*/
private class UILFSystemListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// Set up System Look&Feel
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch ( Exception ex ) {
JOptionPane.showMessageDialog( null, ex.toString() );
}
}
}
/**
* ActionListener for UI L&F Cross Platform
*/
private class UILFCrossPFListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// Set up System Look&Feel
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch ( Exception ex ) {
JOptionPane.showMessageDialog( null, ex.toString() );
}
}
}
/**
* loads image Set
*/
private void getImageSet() {
// Determine imageSet
cardText = "null";
String imageSetPrefix = "m";
if (imageSetArt.isSelected()) {
imageSetPrefix = "a";
}
// Get images
for ( int icnt = 1; icnt <= imageSetNumImages; icnt++ ) {
String imageName = "/"+imageSetPrefix+"Image";
if ( icnt < 10 ) {
imageName += "0";
}
imageName += icnt+".jpg";
URL url = getClass().getResource(imageName);
if ( url != null ) {
images[icnt-1] = Toolkit.getDefaultToolkit().getImage( url );
} else {
images[icnt-1] = null;
}
if ( images[icnt-1] == null) {
cardText = "null";
} else if (imageSetPrefix == "a") {
cardText = sArt;
} else {
cardText = sStandard;
}
imageUsed[icnt-1] = false;
}
}
/**
* Helper function for buildGrid
*/
private void buildGrid() {
int c;
Random qqq = new Random();
// (Re-)Initialize cardGrid
cardGrid.removeAll();
cardGrid.setLayout( new GridLayout(cardRows,cardCols) );
// Initialize all cards and cardImages
for ( c = 0; c < numCards; c++ ) {
cards[c] = new Card( cardText );
cards[c].addActionListener( new CardsListener() );
cardGrid.add(cards[c]);
}
// Initialize imageUsed flag
for ( c = 0; c < imageSetNumImages; c++ ) {
imageUsed[c] = false;
}
// Randomly assign cardImages
for ( int cardCnt2 = 1; cardCnt2 <= numCards/2; cardCnt2++ ) {
// Select random image
int rand1 = Math.abs(qqq.nextInt()) % imageSetNumImages;
// Select first 'real' free image
while ( imageUsed[rand1] ) {
rand1++;
rand1 %= imageSetNumImages;
}
// Mark image as used
imageUsed[rand1] = true;
// Always set a pair of cards with the same value
for ( int pair = 0; pair < 2; pair++ ) {
// Select random card
int rand2 = Math.abs(qqq.nextInt()) % numCards;
// Select first free card
while ( cards[rand2].imageNum!=-1 ) {
rand2++;
rand2 %= numCards;
}
// Assign values
cards[rand2].imageNum = rand1;
cards[rand2].image = images[rand1];
// Java 1.3.1: 'pressedIcon' must be initialized
cards[rand2].setPressedIcon(new ImageIcon(images[rand1]));
}
}
}
/**
* Card class extends JButton
* defined for Card specific Button features
* defines own paint()
*/
private class Card extends JButton {
private static final long serialVersionUID = 373931173203807326L;
private boolean drawImage = false;
private int imageNum = -1;
private Image image = null;
private Color bg;
// Constructor
Card(String text) {
super(text);
bg = getBackground();
}
// Show backside of card ( Card.paint() draws no image)
void showBackside() {
drawImage = false;
repaint();
}
// Show card with image ( Card.paint() draws image )
void showFrontside() {
drawImage = true;
repaint();
}
// paints the image if drawImage is true
public void paint( Graphics g ) {
if ( drawImage ) {
// Get some info
Dimension size = getSize();
int maxWidth = size.width;
int maxHeight = size.height;
// Clear first ...
g.setColor(bg);
g.fillRect(0,0,maxWidth,maxHeight);
// Now get image info
int imageWidth = image.getWidth(this);
int imageHeight = image.getHeight(this);
if (imageWidth > 0 && imageHeight > 0) {
// Determine borders
int dw = 2;
int dh = 2;
while ( dw>0 && maxWidth<=2*dw ) {
dw--;
}
while ( dh>0 && maxHeight<=2*dh ) {
dh--;
}
// Adjust size
maxWidth -= 2*dw;
maxHeight -= 2*dh;
// Determine new image sizes
if ( maxWidth*imageHeight >= maxHeight*imageWidth ) {
imageWidth = ( maxHeight * imageWidth ) / imageHeight;
imageHeight = maxHeight;
} else {
imageHeight = ( maxWidth * imageHeight ) / imageWidth;
imageWidth = maxWidth;
}
// Draw the image
g.drawImage( image, (size.width-imageWidth)/2, (size.height-imageHeight)/2, imageWidth, imageHeight, this );
}
} else {
super.paint(g);
}
}
}
}