import java.lang.*; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.net.*; import java.applet.*; import java.util.*; final public class KanaMatch extends Applet implements Runnable, MouseListener, ActionListener, ItemListener { boolean _fInit = false, _fError = false; Thread _threadLoader = null, _threadTimer = null; Panel _panel; Label _labelScore, _labelTime, _labelBestScore, _labelRound; Color _colorBG; int _iImgSize; int _cxString1, _cxString2, _cxString3, _iCharOffset; final static int _cxRects = 4, _cyRects = 4, _cMaxRects = _cxRects * _cyRects; final static int _iRoundScore = 10000; boolean _afHiragana[] = new boolean[_cMaxRects]; final static int _cFirstRects = 4, _iFirstTimer = 6000; int _cKanaRects = _cFirstRects, _cKanaRemain; int _iScore = 0, _iBestScore = 0, _iTimer = _iFirstTimer, _iTimerRemain, _iRound = 1; long _lTimeStarted; int _iHitRect; boolean _afNotRemoved[] = new boolean[_cMaxRects]; int _aiKana[] = new int[_cMaxRects]; boolean _fInGame = false, _fInRound = false; final static String _strWait = "Now loading images...", _strError = "Image load error!", _strGame = "Click the Start button!"; final static String _astrFonts[] = { "Gothic", "Kaisho", "MaruGothic", "Mincho", "Pop", "Textbook" }; String _strFont = "Textbook"; final static String _strStart = "Start", _strStop = "Stop"; final static int _cKanas = 48, _cTotalKanas = 73; Image _imgsHira[] = new Image[_cKanas], _imgsKata[] = new Image[_cKanas]; final public void init() { Color color = _colorBG = new Color(0xf0, 0xf2, 0xf5); setBackground(color); Panel panel = new Panel(); _panel = panel; panel.setBackground(color); panel.setLayout(new BorderLayout()); Panel panelSub = new Panel(); panelSub.setBackground(color); panelSub.setLayout(new GridLayout(2, 4)); panelSub.add(new Label("Score:", Label.RIGHT)); panelSub.add(_labelScore = new Label("", Label.RIGHT)); panelSub.add(new Label("Time:", Label.RIGHT)); panelSub.add(_labelTime = new Label("", Label.RIGHT)); panelSub.add(new Label("Best score:", Label.RIGHT)); panelSub.add(_labelBestScore = new Label("", Label.RIGHT)); panelSub.add(new Label("Round:", Label.RIGHT)); panelSub.add(_labelRound = new Label("", Label.RIGHT)); panel.add(panelSub, "Center"); panelSub = new Panel(); panelSub.setBackground(color); panelSub.setLayout(new GridLayout(2, 2)); createButton(panelSub, _strStart); createButton(panelSub, _strStop); Choice choice = new Choice(); for (int iIndex = 0; iIndex < _astrFonts.length; iIndex++) { choice.addItem(_astrFonts[iIndex]); } choice.select(_strFont); choice.addItemListener(this); panelSub.add(choice); panel.add(panelSub, "South"); add(panel); setScoreLabel(); setTimerLabel(); Font fontNew = new Font("serif", Font.BOLD, 24), fontOld = getFont(); setFont(fontNew); panel.setFont(fontOld); FontMetrics fm = getFontMetrics(fontNew); _cxString1 = fm.stringWidth(_strWait); _cxString2 = fm.stringWidth(_strError); _cxString3 = fm.stringWidth(_strGame); _iCharOffset = fm.getMaxAscent() - fm.getMaxDescent(); if (!_fInit && !_fError && _threadLoader == null) { (_threadLoader = new Thread(this)).start(); } addMouseListener(this); } final public void start() { requestFocus(); } final public synchronized void doLayout() { if (_panel != null) { Dimension dim = getSize(); int iHeight = dim.height, iPanelHeight = iHeight / 4; _panel.setBounds(0, iHeight - iPanelHeight, dim.width, iPanelHeight); } } final public void stop() { if (_fInGame) { _threadTimer.stop(); _threadTimer = null; endGame(); } } final public void run() { Thread thread = Thread.currentThread(); if (thread == _threadLoader) { try { URL url = getCodeBase(); MediaTracker media = new MediaTracker(this); Image imgHira = getImage(url, _strFont.toLowerCase() + ".gif"), imgKata = getImage(url, _strFont.toLowerCase() + "_k.gif"); media.addImage(imgHira, 0); media.addImage(imgKata, 1); media.waitForAll(); if (media.isErrorAny()) { _fError = true; } else { int iImgSize = _iImgSize = imgHira.getHeight(this); for (int iKanaType = 0; iKanaType <= 1; iKanaType++) { ImageProducer imgprod = (iKanaType == 0 ? imgHira : imgKata).getSource(); Image imgs[] = new Image[_cKanas]; media = new MediaTracker(this); int iKanaIndex = 0; for (int iIndex = 0; iIndex < _cTotalKanas; iIndex++) { if (iIndex < 10 /* a, ka */ || (iIndex >= 15 && iIndex < 20) /* sa */ || (iIndex >= 25 && iIndex < 30) /* ta */ || (iIndex >= 35 && iIndex < 45) /* na, ha */ || iIndex >= 55) /* ma, ya, ra, wa */ { media.addImage(imgs[iKanaIndex] = createImage(new FilteredImageSource (imgprod, new CropImageFilter(iIndex * iImgSize, 0, iImgSize, iImgSize))), iKanaIndex); iKanaIndex++; } } if (iKanaType == 0) { _imgsHira = imgs; } else { _imgsKata = imgs; } media.waitForAll(); } _fInit = true; } } catch (Exception e) { _fError = true; } _threadLoader = null; repaint(); } else if (thread == _threadTimer) { while (_fInRound) { try { Thread.sleep(100); } catch (Exception e) { } int iRemain = _iTimer - ((int)(System.currentTimeMillis() - _lTimeStarted) + 9) / 10; if (iRemain < 0) { iRemain = 0; } _iTimerRemain = iRemain; setTimerLabel(); if (iRemain == 0) { endGame(); break; } } } } final public void paint(Graphics g) { Dimension dim = getSize(); int cx = dim.width; int yString = (dim.height * 3 / 4 + _iCharOffset) / 2; if (_fError) { g.drawString(_strError, (cx - _cxString2) / 2, yString); } else if (_fInit) { if (_fInRound) { int iImgSize = _iImgSize; for (int iIndex = 0; iIndex < _cKanaRects; iIndex++) { if (_afNotRemoved[iIndex]) { g.drawImage((_afHiragana[iIndex] ? _imgsHira : _imgsKata)[_aiKana[iIndex]], (iIndex % _cxRects) * iImgSize, (iIndex / _cxRects) * iImgSize, this); } } if (_iHitRect >= 0) { g.setColor(Color.black); drawHitRect(g, _iHitRect); } } else if (!_fInGame) { g.drawString(_strGame, (cx - _cxString3) / 2, yString); } } else { g.drawString(_strWait, (cx - _cxString1) / 2, yString); } } final private void createButton(Panel panel, String strLabel) { Button button = new Button(strLabel); button.addActionListener(this); panel.add(button); } final void drawHitRect(Graphics g, int iIndex) { int iImgSize = _iImgSize; g.drawRect((iIndex % _cxRects) * iImgSize, (iIndex / _cxRects) * iImgSize, iImgSize - 1, iImgSize - 1); } final void clearHitRect(Graphics g, int iIndex) { int iImgSize = _iImgSize; g.clearRect((iIndex % _cxRects) * iImgSize, (iIndex / _cxRects) * iImgSize, iImgSize, iImgSize); } final void startRound() { int cKanaRects = _cKanaRects; int aiDest[] = new int[cKanaRects]; for (int iIndex = 0; iIndex < cKanaRects; iIndex++) { aiDest[iIndex] = iIndex; _afNotRemoved[iIndex] = true; } for (int iIndex = 0; iIndex < cKanaRects; iIndex++) { int iIndex2 = (int)(Math.random() * (cKanaRects - iIndex)) + iIndex; int iTemp = aiDest[iIndex]; aiDest[iIndex] = aiDest[iIndex2]; aiDest[iIndex2] = iTemp; } long lKanaFlag = 0; /* 64 bits is enough for 48 kanas */ for (int iIndex = 0; iIndex < cKanaRects; iIndex += 2) { int i; do { i = (int)(Math.random() * _cKanas); } while ((lKanaFlag & (1l << i)) != 0); lKanaFlag |= (1l << i); _afHiragana[aiDest[iIndex]] = true; _afHiragana[aiDest[iIndex + 1]] = false; _aiKana[aiDest[iIndex]] = _aiKana[aiDest[iIndex + 1]] = i; } _iHitRect = -1; _cKanaRemain = _cKanaRects; _iTimerRemain = _iTimer; setTimerLabel(); _lTimeStarted = System.currentTimeMillis(); _fInRound = true; (_threadTimer = new Thread(this)).start(); repaint(); } final void endRound() { _fInRound = false; _threadTimer.stop(); _threadTimer = null; if (_iTimerRemain <= 0) { endGame(); } else { _iScore += _iTimerRemain + _iRoundScore; if (_iBestScore < _iScore) { _iBestScore = _iScore; } _iRound++; setScoreLabel(); if (_cKanaRects < _cMaxRects) { _cKanaRects += 2; } else if (_iTimer > 3000) { _iTimer -= 1000; } else if (_iTimer > 1500) { _iTimer -= 250; } else { _iTimer = (_iTimer + 1) * 95 / 100; } startRound(); } } final void startGame() { _cKanaRects = _cFirstRects; _iTimer = _iFirstTimer; _iScore = 0; _iRound = 1; setScoreLabel(); _fInGame = true; startRound(); } final void endGame() { _fInGame = _fInRound = false; repaint(); } final void setTimerLabel() { String str = String.valueOf(Math.max(_iTimerRemain, 0) + 10000); _labelTime.setText(str.substring(1, 3) + '.' + str.substring(3)); } final void setScoreLabel() { _labelScore.setText(String.valueOf(_iScore)); if (_iScore == _iBestScore) { _labelBestScore.setText(String.valueOf(_iBestScore)); } _labelRound.setText(String.valueOf(_iRound)); } final void hitRect(int x, int y) { int iHitRect, iHitRectPrev; if ((x = x / _iImgSize) < _cxRects && (iHitRect = (y / _iImgSize) * _cxRects + x) < _cKanaRects && iHitRect != (iHitRectPrev = _iHitRect) && _afNotRemoved[iHitRect]) { Graphics g = getGraphics(); if (iHitRectPrev >= 0 && _aiKana[iHitRect] == _aiKana[iHitRectPrev]) { clearHitRect(g, iHitRectPrev); clearHitRect(g, iHitRect); _afNotRemoved[iHitRectPrev] = _afNotRemoved[iHitRect] = false; if ((_cKanaRemain = _cKanaRemain - 2) == 0) { endRound(); } iHitRect = -1; } else { if (iHitRectPrev >= 0) { g.setColor(_colorBG); drawHitRect(g, iHitRectPrev); } g.setColor(Color.black); drawHitRect(g, iHitRect); } g.dispose(); _iHitRect = iHitRect; } } final public void mouseClicked(MouseEvent me) { } final public void mouseEntered(MouseEvent me) { } final public void mouseExited(MouseEvent me) { } final public void mousePressed(MouseEvent me) { if (_fInit && _fInRound) { hitRect(me.getX(), me.getY()); } } final public void mouseReleased(MouseEvent me) { } final public void actionPerformed(ActionEvent ae) { if (_fInit) { if (ae.getActionCommand().equals(_strStart)) { startGame(); } else { endGame(); } } } final public void itemStateChanged(ItemEvent ie) { String strFont = (String)ie.getItem(); if (strFont != _strFont) { if (_threadLoader != null) { _threadLoader.stop(); } _fInit = _fError = false; _strFont = strFont; (_threadLoader = new Thread(this)).start(); repaint(); } } }