// My first applet...cut me some slack. Suggestions welcome. // // guichard@whitman.edu import java.awt.*; import java.applet.*; import java.net.URL; public class merlin extends java.applet.Applet { merlin_display the_display; boolean[] current_board = new boolean[9]; boolean[] goal_board = {true,true,true,true,false,true,true,true,true}; boolean[] start_board = new boolean[9]; Label label; framed_canvas framedArea; Image backBuffer; Graphics backGC; Button hint_button, target_button,sound_button; Thread button_thread; SoundList soundList; String successFile = "success.au"; String beepFile = "beep.au"; blinkThread blinker; boolean sounds, buttons; boolean sound_loading = false; final int PLAY = -1; final int BOX0 = 0; final int BOX1 = 1; final int BOX2 = 2; final int BOX3 = 3; final int BOX4 = 4; final int BOX5 = 5; final int BOX6 = 6; final int BOX7 = 7; final int BOX8 = 8; final int RESTART = 9; final int NEWGAME = 10; final int SETGOAL = 11; final int SETSTART= 12; final int HINT = 13; final int QUIT = 14; int mode=PLAY; public void init() // Init all the variables and classes { String str; GridBagLayout gridBag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); try { backBuffer = createImage(size().width, size().height); } catch (Exception e) {} the_display = new merlin_display(backBuffer,goal_board); framedArea = new framed_canvas(the_display); sounds = getboolean("sound",false); buttons = getboolean("buttons",true); setLayout(gridBag); c.fill = GridBagConstraints.BOTH; c.weighty = 5.0; c.weightx = 10.0; c.gridheight = 5; c.gridwidth = 1; gridBag.setConstraints(framedArea, c); add(framedArea); if (buttons) { c.fill = GridBagConstraints.HORIZONTAL; c.insets = new Insets(4,20,0,15); c.weighty = 0.0; c.weightx = 0.4; c.gridheight = 1; c.gridwidth = GridBagConstraints.REMAINDER; //end row hint_button = new Button("Hint"); gridBag.setConstraints(hint_button,c); add(hint_button); c.fill = GridBagConstraints.HORIZONTAL; target_button = new Button("Set"); gridBag.setConstraints(target_button,c); add(target_button); c.fill = GridBagConstraints.HORIZONTAL; sound_button = new Button(); if (sounds) { sound_button.setLabel("Sound Off"); } else { sound_button.setLabel("Sound On"); } gridBag.setConstraints(sound_button,c); add(sound_button); // label = new Label("Click within the framed area."); // c.fill = GridBagConstraints.HORIZONTAL; // c.weightx = 1.0; // c.weighty = 0.0; // gridBag.setConstraints(label, c); // add(label); } validate(); if (sounds) { startLoadingSounds(); } } public boolean action(Event e, Object arg) { Object target = e.target; int h; if (target == hint_button) { if ((h = hint()) >= 0) { blinker = new blinkThread(the_display,h); blinker.start(); } else beep(beepFile); return true; } if (target == target_button) { if (mode == PLAY) { hint_button.disable(); target_button.setLabel("Play"); mode = SETGOAL; } else { hint_button.enable(); target_button.setLabel("Set"); mode = PLAY; } return true; } if (target == sound_button) { sounds = !sounds; if (sounds) { if (!sound_loading) startLoadingSounds(); sound_button.setLabel("Sound Off"); } else { sound_button.setLabel("Sound On"); } return true; } return false; } public boolean getboolean (String att, boolean def) { String parm = getParameter(att); if (parm != null) { if (parm.equals("true")) return true; else if (parm.equals("false")) return false; else return def; } return def; } void startLoadingSounds() { //Start asynchronous sound loading. sound_loading = true; soundList = new SoundList(this, getCodeBase()); soundList.startLoading(successFile); soundList.startLoading(beepFile); } public void toggle_box(int i) { if (current_board[i] = !current_board[i]) the_display.square_on(i); else the_display.square_off(i); } public void do_move(int i) { toggle_box(i); switch(i) { case BOX0 : toggle_box(1); toggle_box(3); toggle_box(4); break; case BOX1 : toggle_box(0); toggle_box(2); break; case BOX2 : toggle_box(1); toggle_box(4); toggle_box(5); break; case BOX3 : toggle_box(0); toggle_box(6); break; case BOX4 : toggle_box(1); toggle_box(3); toggle_box(5); toggle_box(7); break; case BOX5 : toggle_box(2); toggle_box(8); break; case BOX6 : toggle_box(3); toggle_box(4); toggle_box(7); break; case BOX7 : toggle_box(6); toggle_box(8); break; case BOX8 : toggle_box(5); toggle_box(4); toggle_box(7); break; } } public boolean rand01() { if (Math.random() > 0.5) return(true); else return(false); } public boolean goal_reached() { int i; for (i=0; i<=BOX8; i++) if (current_board[i] != goal_board[i]) return(false); return(true); } public void toggle_start(int i) { if (start_board[i] = !start_board[i]) the_display.square_on(i); else the_display.square_off(i); } public void toggle_goal(int i) { if (goal_board[i] = !goal_board[i]) the_display.setgoal(i); else the_display.offgoal(i); the_display.repaint(); } public void start() { // label.setText("Starting game"); start_game(); repaint(); } public void start_game() { int i; for (i=0; i<=BOX8; i++) { if (current_board[i] = start_board[i] = rand01()) the_display.square_on(i); else the_display.square_off(i); } } public void restart_game() { int i; for (i=0; i<=BOX8; i++) if (current_board[i] = start_board[i]) the_display.square_on(i); else the_display.square_off(i); } private boolean XOR (boolean x,boolean y) { return( (x || y) && !(x && y) ); } public int hint() { boolean stat=false; int i; for (i=0; i<6; i++) stat = XOR(stat,XOR( goal_board[i],current_board[i]) ); if (stat) return(1); stat = false; for (i=0; i<3; i++) stat = XOR(stat,XOR(goal_board[3*i], current_board[3*i])); for (i=0; i<3; i++) stat = XOR(stat,XOR(goal_board[3*i+1], current_board[3*i+1])); if (stat) return(3); stat = false; for (i=0; i<3; i++) stat = XOR(stat,XOR(goal_board[3*i+2], current_board[3*i+2])); for (i=0; i<3; i++) stat = XOR(stat,XOR(goal_board[3*i+1], current_board[3*i+1])); if (stat) return(5); stat = false; for (i=3; i<9; i++) stat = XOR(stat,XOR(goal_board[i], current_board[i])); if (stat) return(7); if ( XOR(goal_board[0], current_board[0]) ) return(0); if ( XOR(goal_board[2], current_board[2]) ) return(2); if ( XOR(goal_board[6], current_board[6]) ) return(6); if ( XOR(goal_board[8], current_board[8]) ) return(8); if ( XOR(goal_board[4], current_board[4]) ) return(4); return(-1); } private void beep(String which_clip) { if (sounds) { AudioClip clip; clip = soundList.getClip(which_clip); if (clip != null) { //If the sound is loaded: clip.play(); } } } public boolean gotFocus(Event e, Object what) { if (size().width != backBuffer.getWidth(this)) { try { backBuffer = createImage(size().width, size().height); } catch (Exception exc) {} the_display.newscratch(backBuffer); } return(true); } public boolean mouseDown(java.awt.Event evt, int x,int y) { int i,h; int action=0; boolean return_value=false; // label.setText("Click occurred at coordinate (" // + x + ", " + y + ")."); if (blinker != null) { blinker.hangon(); } action = the_display.getevent(x-4,y-4); switch(action) { case BOX0: case BOX1: case BOX2: case BOX3: case BOX4: case BOX5: case BOX6: case BOX7: case BOX8: switch(mode) { case PLAY: do_move(action); if (goal_reached()) beep(successFile); break; case SETGOAL: toggle_goal(action); break; case SETSTART: toggle_start(action); break; } return_value = true; break; } the_display.repaint(); return(return_value); } } class blinkThread extends Thread { private int box; private merlin_display the_display; private boolean hinting = false; public blinkThread(merlin_display d, int i) { super(); box = i; the_display = d; } public synchronized void run() { int duration = 200; int i; int flashes = 2; hinting = true; the_display.flip(box); the_display.repaint(); for (i=0; i<2*flashes - 1 ; i++) { try { Thread.currentThread().sleep(duration); } catch (InterruptedException e){} the_display.flip(box); the_display.repaint(); } hinting = false; notify(); } public synchronized void hangon() { while (hinting == true) { try { wait(); } catch (InterruptedException e){} } } } // end blinkThread class framed_canvas extends Panel { public framed_canvas(Canvas the_display) { super(); //Set layout to one that makes its contents as big as possible. setLayout(new GridLayout(1,0)); add(the_display); validate(); } public Insets insets() { return new Insets(4,4,4,4); } public void paint(Graphics g) { Dimension d = size(); Color bg = getBackground(); g.setColor(bg); g.draw3DRect(0, 0, d.width - 1, d.height - 1, true); g.draw3DRect(3, 3, d.width - 7, d.height - 7, false); } } class merlin_display extends java.awt.Canvas { merlin_square[] current_board = new merlin_square[9]; Color background_col=Color.white; Image backBuffer; Graphics backGC; boolean background_in=false; int width,height,gutter; public merlin_display(Image img,boolean[] gb) { int i; backBuffer = img; for (i=0; i<9; i++) { current_board[i] = new merlin_square(i,gb[i]); } } public void paint(Graphics g) { update(g); } public void newscratch(Image img) { backBuffer = img; } public void update(Graphics g) { int i,w,h; Graphics backGC; w = size().width; h = size().height; gutter = w/10; width = (w-gutter)/3; height = (h-gutter)/3; gutter = gutter/4; backGC = backBuffer.getGraphics(); backGC.setColor(Color.lightGray); backGC.fillRect(0,0,w,h); for (i=0; i<9; i++) { current_board[i].draw(backGC,width,height,gutter); } g.drawImage(backBuffer, 0, 0, this); } public int getevent(int x, int y) { int i; for (i=0; i<9; i++) { if (current_board[i].is_me(x,y,width,height,gutter)) return(i); } return(-1); } public void flip(int i) { current_board[i].flip(); } public void setgoal(int i) { current_board[i].setgoal(true); } public void offgoal(int i) { current_board[i].setgoal(false); } public void square_on(int i) { current_board[i].square_on(); } public void square_off(int i) { current_board[i].square_off(); } } class merlin_square { private int number,width,height,gutter; private Color on_color=Color.red; private Color off_color=Color.blue; private Color my_color; private boolean goal; public merlin_square(int i, boolean g) { number = i; goal = g; } public boolean is_me(int x, int y,int width,int height,int gutter) { if ( ( (number%3)*(gutter+width) + gutter <= x ) && ( x <= (1+number%3)*(gutter+width) ) && ( (number/3)*(gutter+height) + gutter <= y ) && ( y <= (1+number/3)*(gutter+height) ) ) return(true); else return(false); } public void draw (Graphics g,int width,int height,int gutter) { // g.setColor(my_color); // g.fillRect(gutter+(number%3)*(gutter+width), // gutter+(number/3)*(gutter+height),width,height); // if (goal) { // g.setColor(Color.black); // } else { // g.setColor(Color.lightGray); // } // g.drawRect(gutter+(number%3)*(gutter+width)-2, // gutter+(number/3)*(gutter+height)-2,width+3,height+3); // g.drawRect(gutter+(number%3)*(gutter+width)-1, // gutter+(number/3)*(gutter+height)-1,width+1,height+1); if (goal) { g.setColor(on_color); } else { g.setColor(off_color); } g.fillRect(gutter+(number%3)*(gutter+width), gutter+(number/3)*(gutter+height),width,height); g.setColor(my_color); g.fillOval(gutter+(number%3)*(gutter+width), gutter+(number/3)*(gutter+height),width,height); } public void flip () { if (my_color == on_color) { my_color = off_color; } else { my_color = on_color; } } public void setgoal (boolean b) { goal = b; } public void square_on () { my_color= on_color; } public void square_off () { my_color= off_color; } } class SoundLoader extends Thread { Applet applet; SoundList soundList; URL baseURL; String relativeURL; public SoundLoader(Applet applet, SoundList soundList, URL baseURL, String relativeURL) { this.applet = applet; this.soundList = soundList; this.baseURL = baseURL; this.relativeURL = relativeURL; setPriority(MIN_PRIORITY); start(); } public void run() { AudioClip audioClip = applet.getAudioClip(baseURL, relativeURL); //AudioClips load too fast for me! //Simulate slow loading by adding a delay of up to 10 seconds. // try { // sleep((int)(Math.random()*10000)); // } catch (InterruptedException e) {} soundList.putClip(audioClip, relativeURL); } } class SoundList extends java.util.Hashtable { Applet applet; URL baseURL; public SoundList(Applet applet, URL baseURL) { super(5); //Initialize Hashtable with capacity of 5 entries. this.applet = applet; this.baseURL = baseURL; } public void startLoading(String relativeURL) { new SoundLoader(applet, this, baseURL, relativeURL); } public AudioClip getClip(String relativeURL) { return (AudioClip)get(relativeURL); } public void putClip(AudioClip clip, String relativeURL) { put(relativeURL, clip); } }