import java.applet.Applet; import java.awt.*; import java.io.*; import java.net.*; import java.util.StringTokenizer; /*----------------------------------------------------------------------------- LightsOut -----------------------------------------------------------------------------*/ public class LightsOut extends Applet { Board board; EntranceDialog eDialog; StageDialog sDialog; boolean playerIdentified = false; String nickName, passwd; String LIGHTSOUTDIR = "---"; String SCOREDIR = "---"; Label stageLabel, scoreLabel, forPerfectLabel; public void init() { setBackground(new Color(34, 34, 34)); setForeground(Color.white); // Graphics g = getGraphics(); setFont(new Font("Helvetica", Font.PLAIN, 14)); board = new Board(this); board.init(); stageLabel = new Label("Stage: --"); scoreLabel = new Label("Score: ---"); forPerfectLabel = new Label("Steps for Perfect: ---"); // stageLabel.setForeground(Color.white); // System.out.println((stageLabel.getForeground()).toString()); // scoreLabel.setForeground(Color.white); // forPerfectLabel.setForeground(Color.white); setLayout(new BorderLayout()); Panel p1 = new Panel(); stageLabel.setForeground(Color.white); // p1.setLayout(new FlowLayout(FlowLayout.LEFT)); p1.add(stageLabel); p1.add(scoreLabel); p1.add(forPerfectLabel); add("North", p1); add("Center", board); Panel p2 = new Panel(); p2.add(new Button("Enter")); p2.add(new Button("Restart")); p2.add(new Button("Select Stage")); Checkbox soundCheckbox = new Checkbox("Sound"); soundCheckbox.setState(true); p2.add(soundCheckbox); add("South", p2); eDialog = new EntranceDialog(this); sDialog = new StageDialog(this); board.reset(); } public boolean action(Event e, Object arg) { if (e.target instanceof Button) { if ("Restart".equals(arg)) { if (playerIdentified) { board.reset(); } } else if ("Enter".equals(arg)) { eDialog.show(); } else if ("Select Stage".equals(arg)) { if (playerIdentified) { sDialog.setItems(); sDialog.show(); } } return true; } else if (e.target instanceof Checkbox) { board.playSound = ((Boolean)arg).booleanValue(); return true; } return false; } public void playerIdent() { URL identUrl; DataInputStream dis; String paramLine, line; nickName = eDialog.nickName.getText(); passwd = eDialog.passwd.getText(); paramLine = "?"; paramLine += "nickname=" + nickName; paramLine += "&password=" + passwd; playerIdentified = false; try { identUrl = new URL("----" + paramLine); dis = new DataInputStream(identUrl.openStream()); while ((line = dis.readLine()) != null) { if (line.startsWith("OK")) { playerIdentified = true; } } } catch (IOException e) { e.printStackTrace(); } if (playerIdentified) { // 今までクリアしたステージの数 try { URL scoreUrl = new URL(SCOREDIR + nickName); dis = new DataInputStream(scoreUrl.openStream()); line = dis.readLine(); int stage = Integer.parseInt(line) + 1; if (stage > 50) { stage = 50; } board.readStage(stage); } catch (IOException e) { e.printStackTrace(); } } else { board.reset(); } } } /*----------------------------------------------------------------------------- ******************************************************************************* Board ******************************************************************************* -----------------------------------------------------------------------------*/ class Board extends Canvas { LightsOut parent; int selectedStage; int minSteps, step, score; boolean status[]; boolean initialStatus[]; Image buttonOn, buttonOff; int rows = 5, columns = 5; int buttonWidth, buttonHeight; int topMargin = 0, leftMargin = 20, buttonInterval = 5; boolean playSound = true; Board(LightsOut p) { parent = p; } /*----------------------------------------------------------------------------- ボタンのイメージを読み込む -----------------------------------------------------------------------------*/ public void init() { MediaTracker tracker = new MediaTracker(parent); buttonOn = parent.getImage(parent.getCodeBase(), "../images/on4.gif"); tracker.addImage(buttonOn, 0); buttonOff = parent.getImage(parent.getCodeBase(), "../images/off4.gif"); tracker.addImage(buttonOff, 0); try { tracker.waitForID(0); } catch (InterruptedException e) { return; } buttonWidth = buttonOn.getWidth(parent); buttonHeight = buttonOff.getHeight(parent); // System.out.println(size().width + "\n"); status = new boolean[columns * rows]; initialStatus = new boolean[columns * rows]; for (int i = 0; i < columns * rows; i++) { initialStatus[i] = false; } } /*----------------------------------------------------------------------------- ステージ番号を受け取り、その面データを読み込んで initialStatus[] に保持する ステージ番号は public な selectedStage にコピー 最後にボードをリセット -----------------------------------------------------------------------------*/ public void readStage(int stage) { selectedStage = stage; URL stageUrl; DataInputStream dis; String line; StringTokenizer st; int i; try { stageUrl = new URL(parent.LIGHTSOUTDIR + "files/stage"); dis = new DataInputStream(stageUrl.openStream()); i = 0; do { line = dis.readLine(); i++; } while (i < selectedStage); st = new StringTokenizer(line); minSteps = Integer.parseInt(st.nextToken()); for (i = 0; i < rows * columns; i++) { initialStatus[i] = Integer.parseInt(st.nextToken()) == 1 ? true : false; } } catch (IOException e) { e.printStackTrace(); } reset(); } /*----------------------------------------------------------------------------- step, score を初期値に ボードをリセット ボードを書き直す -----------------------------------------------------------------------------*/ public void reset() { step = 0; score = 100; for (int i = 0; i < rows * columns; i++) { status[i] = initialStatus[i]; } setLabels(); repaint(); } /*----------------------------------------------------------------------------- ボード全体を描く -----------------------------------------------------------------------------*/ public void paint(Graphics g) { leftMargin = (size().width - (buttonWidth * columns + buttonInterval * (columns - 1))) / 2; if (parent.playerIdentified) { for (int by = 0; by < rows; by++) { for (int bx = 0; bx < columns; bx++) { drawButton(g, bx, by); } } } else { g.setFont(new Font("Helvetica", Font.PLAIN, 16)); g.drawString("Click \"Enter\" to", leftMargin, 40); g.drawString("Input Nickname and Password.", leftMargin, 60); g.drawString("If you want to register,", leftMargin, 80); g.drawString("Click \"Enter\" - \"Register\" .", leftMargin, 100); } } /*----------------------------------------------------------------------------- ボタンが押されたらライト反転メソッドを呼ぶ クリアしてたら finishStage を呼ぶ -----------------------------------------------------------------------------*/ public boolean mouseDown(Event e, int px, int py) { if (parent.playerIdentified) { if (toggle(px, py)) { finishStage(); } } return true; } /*----------------------------------------------------------------------------- クリアしたときの処理 ちゃんとクリアしたのなら サーバーにクリアしたときのデータを送る (nickName, passwd, selectedStage, minSteps, step) 次のステージに進む -----------------------------------------------------------------------------*/ private void finishStage() { URL finishUrl; DataInputStream dis; String line; // クリア時文字列の生成 Graphics g = getGraphics(); g.setFont(new Font("Helvetica", Font.BOLD, 24)); FontMetrics fm = g.getFontMetrics(); int x, y, i; String str[]; str = new String[5]; str[0] = "Steps for Perfect: " + minSteps; str[1] = "Steps You Needed: " + step; if (step - minSteps <= 10) { str[2] = "Score: " + score; str[3] = step == minSteps ? "Excellent!!" : "Good!"; str[4] = "Try Next Stage"; } else { str[2] = "Score: ---"; str[3] = " "; str[4] = "Retry This Stage"; } // 画面の描き直し repaint(); setLabels(); for (i = 0; i < 5; i++) { x = (size().width - fm.stringWidth(str[i])) / 2; y = 40 + (fm.getHeight() + 20) * i; g.drawString(str[i], x, y); } if (step - minSteps <= 10) { // 最小手数+10 以内でクリア // parent.play(parent.getCodeBase(), "../sounds/daytona13.au"); // QUERY_STRING の生成 String paramLine = "?"; paramLine += "nickname=" + parent.nickName; paramLine += "&password=" + parent.passwd; paramLine += "&stage=" + selectedStage; paramLine += "&minsteps=" + minSteps; paramLine += "&steps=" + step; // System.out.println(paramLine + "\n"); // アクセス try { finishUrl = new URL("---" + paramLine); dis = new DataInputStream(finishUrl.openStream()); while ((line = dis.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } // ステージを進める int stage = selectedStage; if (stage < 50) { stage += 1; } readStage(stage); } else { reset(); } // しばらく待つ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } /*----------------------------------------------------------------------------- 親のラベルを書き換える -----------------------------------------------------------------------------*/ private void setLabels() { parent.stageLabel.setText("Stage: " + selectedStage); if ((minSteps - step) >= -10) { parent.forPerfectLabel.setText("Steps for Perfect:" + (minSteps - step)); parent.scoreLabel.setText("Score: " + score); } else { parent.forPerfectLabel.setText("Steps for Perfect: ---"); parent.scoreLabel.setText("Score: ---"); } } /*----------------------------------------------------------------------------- px, py : ポインタの座標 → へんな場所だったら何もしない 押されたボタンとその周囲を反転してそのボタンを描き直す ボタンの状況を調べて全部消えてたら true を返す -----------------------------------------------------------------------------*/ private boolean toggle(int px, int py) { int bx = pointer2buttonX(px); int by = pointer2buttonY(py); boolean cleared = true; if (bx < 0 || by < 0 ) { return false; } for (int iy = 0; iy < rows; iy++) { for (int ix = 0; ix < columns; ix++) { if (ix == bx && iy == by - 1 || ix == bx - 1 && iy == by || ix == bx && iy == by || ix == bx + 1 && iy == by || ix == bx && iy == by + 1) { status[iy * columns + ix] = status[iy * columns + ix] ? false : true; drawButton(getGraphics(), ix, iy); } if (status[iy * columns + ix]) { // 一個でもランプがついてたら cleared = false; // cleared は false } } } if (playSound) { parent.play(parent.getCodeBase(), "../sounds/srally1.au"); } step++; if ((minSteps - step) < 0 && score > 50) { score -= 5; } setLabels(); return cleared; } /*----------------------------------------------------------------------------- ボタンを一個だけ描く -----------------------------------------------------------------------------*/ private void drawButton(Graphics g, int bx, int by) { Image im = status[by * columns + bx] ? buttonOn : buttonOff; g.drawImage(im, bx * (buttonWidth + buttonInterval) + leftMargin, by * (buttonHeight + buttonInterval) + topMargin, parent); } /*----------------------------------------------------------------------------- マウスカーソルの座標からボタンの座標を返す(x y 別) -----------------------------------------------------------------------------*/ private int pointer2buttonX(int px) { int x = (px - leftMargin) / (buttonWidth + buttonInterval); if (px - (leftMargin + x * (buttonWidth + buttonInterval)) > buttonWidth || x < 0 || columns <= x || px - leftMargin < 0) { x = -1; } return x; } private int pointer2buttonY(int py) { int y = (py - topMargin) / (buttonHeight + buttonInterval); if (py - (topMargin + y * (buttonHeight + buttonInterval)) > buttonHeight || y < 0 || rows <= y || py - topMargin < 0) { y = -1; } return y; } } /*----------------------------------------------------------------------------- ******************************************************************************* Entrance Dialog ******************************************************************************* -----------------------------------------------------------------------------*/ class EntranceDialog extends Frame { TextField nickName; TextField passwd; LightsOut parent; public EntranceDialog(LightsOut p) { parent = p; setTitle("Player Identification"); setFont(new Font("Helvetica", Font.PLAIN, 14)); setLayout(new BorderLayout()); nickName = new TextField(20); passwd = new TextField(20); passwd.setEchoCharacter('*'); Panel nn = new Panel(); nn.setLayout(new FlowLayout(FlowLayout.RIGHT)); nn.add(new Label("Nickname:", Label.RIGHT)); nn.add(nickName); Panel pw = new Panel(); pw.setLayout(new FlowLayout(FlowLayout.RIGHT)); pw.add(new Label("Password:", Label.RIGHT)); pw.add(passwd); Panel buttons = new Panel(); buttons.add(new Button("OK")); buttons.add(new Button("Cancel")); buttons.add(new Button("Register")); add("North", nn); add("Center", pw); add("South", buttons); pack(); } public boolean action(Event e, Object arg) { if (e.target instanceof Button) { if ("OK".equals(arg)) { if ((nickName.getText()).length() > 0 && (passwd.getText()).length() > 0) { hide(); parent.playerIdent(); } } else if ("Cancel".equals(arg)) { hide(); } else if ("Register".equals(arg)) { hide(); try { parent.getAppletContext().showDocument (new URL("---")); } catch (MalformedURLException ee) { ee.printStackTrace(); } } else { } } else if (e.target instanceof TextField) { if ((nickName.getText()).length() > 0 && (passwd.getText()).length() > 0) { hide(); parent.playerIdent(); } } return true; } public void show() { nickName.setText(""); passwd.setText(""); super.show(); } } /*----------------------------------------------------------------------------- ******************************************************************************* Select Stage Dialog ******************************************************************************* -----------------------------------------------------------------------------*/ class StageDialog extends Frame { LightsOut parent; List stageList; public StageDialog(LightsOut p) { parent = p; setTitle("Select Stage"); setFont(new Font("Helvetica", Font.PLAIN, 14)); setLayout(new BorderLayout()); stageList = new List(10, false); add("Center", stageList); Panel buttons = new Panel(); buttons.add(new Button("OK")); buttons.add(new Button("Cancel")); add("South", buttons); } public void setItems() { String line, listLine; int score; stageList.clear(); // list の中身を全部消す try { URL scoreUrl = new URL(parent.SCOREDIR + parent.nickName); DataInputStream dis = new DataInputStream(scoreUrl.openStream()); line = dis.readLine(); int maxStage = Integer.parseInt(line); for (int i = 0; i < maxStage; i++) { line = dis.readLine(); score = Integer.parseInt(line); listLine = "Stage " + (i + 1) + " (score = " + score + ")"; stageList.addItem(listLine); } if (maxStage < 50) { listLine = "Stage " + (maxStage + 1); // まだクリアしてないステージ stageList.addItem(listLine); } } catch (IOException e) { e.printStackTrace(); } pack(); stageList.resize(stageList.preferredSize(10)); // これやんないとボタンが隠れる resize((int)(size().width * 1.5), size().height); // List の端が切れないように stageList.select(parent.board.selectedStage - 1); // index は 0 から } public boolean action(Event e, Object arg) { if (e.target instanceof Button && "Cancel".equals(arg)) { hide(); } else if ((e.target instanceof Button && "OK".equals(arg) || e.target instanceof List) && stageList.getSelectedIndex() > -1) { hide(); parent.board.readStage(stageList.getSelectedIndex() + 1); } return true; } }