/*------------------------------------------------------------------------ * CS60 Spring, 2000 * Name : SooYoung Jung * Filename : Spampede.java * Assignment : 6 * Date : March 4, 2000 * Description : The Spampede applet gives a user control over a spam- * seeking centipede. Key presses from the keyboard change * the direction of the centipede's movement in order to * intersect spams that appear at random places on the * screen. For each spam consumed, the centipede grows by * one segment (a segment is simply one MazeCell). */ import java.applet.Applet; import java.awt.*; import java.awt.event.*; import java.io.PrintStream; import java.util.EventObject; import java.util.Random; public class Spampede extends Applet implements ActionListener, KeyListener, Runnable { /* * data members */ // Store Whole maze cell. Maze M; // Store spampede. Deque Centipede; // Store spam position. Queue Spam; Graphics graphics; // enables the uses of all drawing routines Image image; // off-screen buffer // 50 milliseconds between updates int sleepTime = 50; // gap between making new spam. int newSpamTime = 50; int cycleNum = 0; // number of update cycles so far int sqSize = 10; // set the square size. int score = 0; // set the square size. /* * these are AWT components in the applet */ private Button startButton; private Button pauseButton; /* * here is a default color for drawing */ Color backColor = Color.white; Color wallColor = Color.blue; Color headColor = Color.black; Color bodyColor = Color.gray; Color spamColor = Color.orange; Color setColor; // define direction. static final int EAST = 0; static final int WEST = 1; static final int NORTH = 2; static final int SOUTH = 3; static final int FORWARD = 0; static final int REVERSE = 1; int spampedeDirection; int direction; // Current int nextX; int nextY; /* * Events that occur get sent here and are handled */ public void actionPerformed(ActionEvent evt) { Object source = evt.getSource(); // When startButton is clicked // start to run spampede. if (source == startButton) { running = true; go(); } // When pauseButton is clicked // pause spampede. if (source == pauseButton) { pause(); } repaint(); } /* * A method to clear the applet's drawing area * It also draws a single-pixel frame around it. */ void clear() { graphics.setColor(Color.white); graphics.fillRect(0, 0, getSize().width, getSize().height); graphics.setColor(Color.black); graphics.drawString("Key : i:North, j:East, k:South", 60, 40); graphics.drawString("l:East, r:reverse", 230, 40); } /* * Things you want to happen at each update step * should be placed in this method. */ void cycle() { updateCent(); updateSpam(); clear(); drawMaze(M); score(); cycleNum++; } void score() { graphics.setColor(Color.red); graphics.drawString("Score : " + score, 350, 40); } /* * A method to draw Maze. */ void drawMaze(Maze MM) { for( int i = 0; i < MM.width; i++ ) { for( int j = 0; j < MM.height; j++ ) { setColor = backColor; MazeCell temp = MM.grid[j][i]; // S as spam if ( temp.getContents() == 'S' ) setColor = spamColor; // H as spampede's head if ( temp.getContents() == 'H' ) setColor = headColor; // B as spampede's tail if ( temp.getContents() == 'B' ) setColor = bodyColor; // | and - as wall if ( temp.getContents() == '|' || temp.getContents() == '-' ) setColor = wallColor; graphics.setColor(setColor); if ( temp.getContents() == 'H' ) graphics.fillOval(50 + sqSize * i, 50 + sqSize * j, sqSize, sqSize); else graphics.fillRect(50 + sqSize * i, 50 + sqSize * j, sqSize, sqSize); } } } /* * Initialize the applet */ public void init() { /* create an off-screen image for drawing */ image = createImage(getSize().width, getSize().height); graphics = image.getGraphics(); clear(); /* create each component and add it to the applet */ this.addKeyListener(this); startButton = new Button("Start"); startButton.addActionListener(this); startButton.addKeyListener(this); add(startButton); pauseButton = new Button("Pause"); pauseButton.addActionListener(this); add(pauseButton); M = new Maze(Maze.spampedeMaze); drawMaze(M); resetCentipede(); // Creating new queue for storing spams. Spam = new Queue(); repaint(); running = true; } /* * Key event handlers */ public void keyPressed(KeyEvent keyevent) { // User want to move spampede to North if (keyevent.getKeyChar() == 'i' || keyevent.getKeyChar() == 'I') spampedeDirection = NORTH; // User want to move spampede to West if (keyevent.getKeyChar() == 'j' || keyevent.getKeyChar() == 'J') spampedeDirection = WEST; // User want to move spampede to South if (keyevent.getKeyChar() == 'k' || keyevent.getKeyChar() == 'K') spampedeDirection = SOUTH; // User want to move spampede to East if (keyevent.getKeyChar() == 'l' || keyevent.getKeyChar() == 'L') spampedeDirection = EAST; // User want to move spampede to reverse direction if (keyevent.getKeyChar() == 'r' || keyevent.getKeyChar() == 'R') reverse(); repaint(); } // No handler need for next two event. public void keyReleased(KeyEvent keyevent) { } public void keyTyped(KeyEvent keyevent) { } /* * reverse method reverse the spampede's direction * i.e North <-> South, West <-> East. */ void reverse() { // We know the Head will be the end of the body. M.grid[nextY][nextX].setContents('B'); // Change the end of body to head. MazeCell reverseCell = (MazeCell)Centipede.back.data; reverseCell.setContents('H'); // Change direction to opposite way. if (spampedeDirection == EAST || spampedeDirection == NORTH) spampedeDirection++; else spampedeDirection--; // remember next forwarding cell. nextY = reverseCell.y(); nextX = reverseCell.x(); // Change direction to FORWARD after changing // direction. direction = FORWARD; } /* * updateCent makes spampede to move to next positon depended on * direction. This direction is determined by keyEvent. */ void updateCent() { MazeCell tempCel_0 = M.grid[nextY][nextX]; M.grid[nextY][nextX].setContents('B'); if (spampedeDirection == EAST) tempCel_0 = M.grid[nextY][++nextX]; else if (spampedeDirection == WEST) tempCel_0 = M.grid[nextY][--nextX]; else if (spampedeDirection == NORTH) tempCel_0 = M.grid[--nextY][nextX]; else if (spampedeDirection == SOUTH) tempCel_0 = M.grid[++nextY][nextX]; // After change direction, determine // whether spampede need to expand or just // move forward or backward. if(direction == FORWARD) { // reduce tail.when next cell is nothing if (tempCel_0.getContents() == ' ' ) { MazeCell tempCel_1 = (MazeCell)Centipede.dequeueBack(); tempCel_1.setContents(' '); Centipede.enqueueFront(tempCel_0); tempCel_0.setContents('H'); } // expand head.when next cell is Spam else if (tempCel_0.getContents() == 'S' ) { Centipede.enqueueFront(tempCel_0); tempCel_0.setContents('H'); score++; } resetCentipede(); } else { // reduce tail.when next cell is nothing if (tempCel_0.getContents() == ' ' ) { MazeCell tempCel_2 = (MazeCell)Centipede.dequeue(); tempCel_2.setContents(' '); Centipede.enqueue(tempCel_0); tempCel_0.setContents('H'); } // expand head.when next cell is Spam else if (tempCel_0.getContents() == 'S' ) { Centipede.enqueue(tempCel_0); tempCel_0.setContents('H'); score++; } resetCentipede(); } //graphics.drawString("Key ",300,200); } /* * resetCentipede is reset Centipede deque at the beginning. * Also, when spampede dies, creal Maze so in the next game * spampede will not remain in Maze. */ void resetCentipede() { // If spampede dies, clear Centipede Deque // and Maze Cell that occupied by Centiped. if(Centipede != null) { MazeCell tempCell; while (!Centipede.is_empty()) { tempCell = (MazeCell)Centipede.dequeue(); tempCell.setContents(' '); } score = 0; } // Making initial spampede with head and body. Centipede = new Deque(); Centipede.enqueue(M.grid[1][2]); M.grid[1][2].setContents('H'); Centipede.enqueue(M.grid[1][1]); M.grid[1][1].setContents('B'); // Setting initial direction that is facing EAST. spampedeDirection = EAST; direction = FORWARD; // Next candidate moving cell. nextX = 2; nextY = 1; } /* * updateSpam makes spams for every certain cycle. */ void updateSpam() { if(cycleNum % newSpamTime == 0) { cycleNum = 0; if(Spam.is_empty()) { // If there is no more spams in queue, // make a little more spams. for(int i = 0; i < 5; i++) makeNewSpam(Spam); } else { // Keep renew spam position. makeNewSpam(Spam); MazeCell mazecell = (MazeCell)Spam.dequeue(); mazecell.setContents(' '); } } } /* * makeNewSpam makes a new spam randomly. */ void makeNewSpam(Queue queue) { // Range should be with in height and width // except for both side walls. int i = (int)(1.0D + (double)(M.height - 2) * Math.random()); int j = (int)(1.0D + (double)(M.width - 2) * Math.random()); // If MazeCell that randomly picked up // has any contents(not wall, spampede, or spam), // then it can be new spam. MazeCell tempCell = M.grid[i][j]; if(tempCell.getContents() == ' ') { tempCell.setContents('S'); queue.enqueue(tempCell); } } /* * The following two methods are overriding those from * the Applet base class. You will not need to call these * explicitly, but they will be used when you do call * * repaint(); */ public void update(Graphics g) { paint(g); } public void paint(Graphics g) { g.drawImage(image, 0, 0, null); } /* * The following methods are used to implement the * Runnable interface and to support pausing and * resuming the applet. * You may use these as they are, but of course * may change them if you'd like. */ /* * This is the method that calls the "cycle()" * method every so often (every sleepTime milliseconds). */ Thread thread; // the thread controlling the updates boolean threadSuspended; // whether or not the thread is suspended boolean running; // whether or not the thread is stopped public void run() { while(running) { try { if(thread != null) { thread.sleep(sleepTime); synchronized(this) { while(threadSuspended) wait(); } } } catch(InterruptedException e) { ; } cycle(); repaint(); } thread = null; } /* This is the method attached to the "Start" button */ public synchronized void go() { if (thread == null) { thread = new Thread(this); running = true; thread.start(); threadSuspended = false; } else { threadSuspended = false; } notify(); } /* This is the method attached to the "Pause" button */ void pause() { if (thread == null) { ; } else { threadSuspended = true; } } /* * This is a method called when you leave the page * that contains the applet. It stops the thread altogether. */ public synchronized void stop() { running = false; notify(); } /* This is the end of the Spampede class */ } //-------------------------------------------------------------------------- /* After this comment are the Maze and MazeCell, * Queue and QCell classes * * Feel free to replace these with your own classes. */ class MazeCell { boolean marked; char contents; MazeCell parent; int x,y; MazeCell(int x, int y) { marked = false; contents = ' '; parent = null; this.x = x; this.y = y; } boolean isMarked() {return marked;} boolean isFree() {return (!marked && !this.isWall());} boolean isWall() {return (contents == '|' || contents == '-');} void mark() {marked = true;} void unmark() {marked = false;} char getContents() {return contents;} void setContents(char c) {contents = c;} MazeCell getParent() {return parent;} void setParent(MazeCell p) {parent = p;} int x() {return x;} int y() {return y;} void setPosition(int x, int y) {this.x = x; this.y = y;} } class Maze { String name; MazeCell[][] grid; int width; int height; int startx; int starty; int spamx; int spamy; /* Mazes */ static String[] spampedeMaze = { "spampedeMaze", "30", "50", "|------------------------------------------------|", "| |", "| |", "| |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| | |", "| |", "| |", "| |", "|------------------------------------------------|", }; /* Constructor */ Maze(String[] info) { name = info[0]; height = (new Integer(info[1])).intValue(); width = (new Integer(info[2])).intValue(); grid = new MazeCell[height][width]; char c; for (int i=0 ; i= 0 && (W = grid[y][x-1]).isFree() ) { W.mark(); W.setParent(cmc); Q.enqueue(W); } // Northern neighbor if ( y-1 >= 0 && (N = grid[y-1][x]).isFree() ) { N.mark(); N.setParent(cmc); Q.enqueue(N); } // Southern neighbor if ( y+1 < height && (S = grid[y+1][x]).isFree() ) { S.mark(); S.setParent(cmc); Q.enqueue(S); } } System.out.println("Maze not solvable!\n"); } /* public static void main(String[] arg) { Maze M = new Maze(ouchMaze); System.out.println(M); M.solve(); System.out.println(M); } */ } /* * QCell class */ class QCell { /* * The data members */ Object data; QCell next; /* * Constructor */ QCell(Object o, QCell next) { this.data = o; this.next = next; } } class Queue { /* * The data members */ QCell front; QCell back; Queue() { front = null; back = null; } boolean is_empty() {return (front == null);} static boolean is_empty(Queue Q) {return Q.is_empty();} void enqueue(Object o) { QCell newCell = new QCell(o,null); if (back == null) { back = front = newCell; } else { back.next = newCell; back = newCell; } } static void enqueue(Object o, Queue Q) { Q.enqueue(o); } Object dequeue() { Object retVal = front.data; front = front.next; if (front == null) back = null; return retVal; } static Object dequeue(Queue Q) { return Q.dequeue(); } /* * toString() is a nonstatic method that returns a string representation of * the Queue that invokes this method. It may be invoked implicitly * when a Queue is an argument to the + operator and another String. */ public String toString() { StringBuffer theFullAnswer = new StringBuffer(); // now it's "" theFullAnswer.append("The Queue is\n"); QCell current = this.front; while (current != null) { theFullAnswer.append(" " + current.data + "\n"); current = current.next; } theFullAnswer.append("\n"); return theFullAnswer.toString(); } /* MAIN */ /* public static void main(String[] arg) { Queue Q = new Queue(); Q.enqueue(new Double(12.0)); System.out.println(Q); Q.enqueue(new Double(14.25)); System.out.println(Q); System.out.println("Q.dequeue() is " + Q.dequeue()); System.out.println(Q); System.out.println("Q.dequeue() is " + Q.dequeue()); System.out.println(Q); enqueue(new Double(-40.0),Q); Double D = new Double(1000.0); enqueue(D,Q); System.out.println(Q); System.out.println("dequeue(Q) is " + dequeue(Q)); System.out.println(Q); } */ } class DCell extends QCell { DCell prev; DCell(Object o, DCell prev, DCell next) { super(o,next); this.prev = prev; } } class Deque extends Queue { // no extra data members ! Deque() { super(); } void enqueue(Object o) // overriding Queue's enqueue { if (is_empty()) { DCell dc = new DCell(o,null,null); front = back = dc; } else { DCell dc = new DCell(o,(DCell)back,null); back.next = dc; back = dc; } } void enqueueFront(Object o) // overriding Queue's enqueue { if (is_empty()) { DCell dc = new DCell(o,null,null); front = back = dc; } else { DCell dc = new DCell(o,null,(DCell)front); ((DCell)front).prev = dc; front = dc; } } Object dequeue() { Object data = front.data; front = front.next; if (front == null) { back = null; } else { ((DCell)front).prev = null; } return data; } Object dequeueBack() { Object data = back.data; back = ((DCell)back).prev; if (back == null) { front = null; } else { back.next = null; } return data; } static void enqueue(Object o, Deque D) { D.enqueue(o); } static void enqueueFront(Object o, Deque D) { D.enqueueFront(o); } static Object dequeue(Deque D) { return D.dequeue(); } static Object dequeueBack(Deque D) { return D.dequeueBack(); } }