/******************************** * CS2510 Fall 2011 * Lecture #8 * Designing Snake ********************************/ import tester.*; import image.*; import world.*; // Represents the SnakeGame world class SnakeWorld extends World { Snake snake; Food food; /** * Template * * Fields * snake -- Snake * food -- Food * * Methods * onTick -- SnakeWorld * onDraw -- Scene * onKey -- SnakeWorld */ SnakeWorld(Snake snake, Food food) { this.snake = snake; this.food = food; } // Sets the speed of the world public double tickRate() { return 0.25; } // Draws the world with the snake and food public Scene onDraw() { return snake.draw(food.draw(new EmptyScene(food.WIDTH,food.HEIGHT))); } // Makes our snake move each tick public SnakeWorld onTick() { return new SnakeWorld(snake.move(),food); } // Sets the direction of the snake based on key presses public SnakeWorld onKey(String str) { return new SnakeWorld(snake.changeDir(str),food); } } // Represents a grid position in our world abstract class AGrid extends Posn { // Some constants about our grid int CELL_SIZE = 20; int WIDTH_INCELLS = 20; int HEIGHT_INCELLS = 20; int WIDTH = WIDTH_INCELLS * CELL_SIZE; int HEIGHT = HEIGHT_INCELLS * CELL_SIZE; AGrid(int x, int y) { super(x, y); } /** * Template * Fields: * this.x -- int * this.y -- int * Methods: * this.cell2Pixel() -- Posn */ // Converts a cell to its pixel position Posn cell2Pixel() { return new Posn(CELL_SIZE * x + CELL_SIZE/2, CELL_SIZE * y + CELL_SIZE/2); } } // Represents a food object in our SnakeWorld class Food extends AGrid { Food(int x, int y) { super(x, y); } /** * Template * * Fields: * this.x -- int * this.y -- int * Methods: * this.draw(Scene) -- Scene */ // Draws the Food in the scene Scene draw(Scene s) { return s.placeImage(new Circle(CELL_SIZE/2-1, "solid", "purple"), this.cell2Pixel()); } } // Represents a Segment in our SnakeWorld class Seg extends AGrid { Seg(int x, int y) { super(x, y); } /** * Template * * Fields: * this.x -- int * this.y -- int * Methods: * this.draw(Scene) -- Scene * this.move(String) -- Seg */ // Draws the segment into the scene Scene draw(Scene s) { return s.placeImage(new Circle(CELL_SIZE/2-1, "solid", "black"), this.cell2Pixel()); } // Returns a moved segment based on key presses Seg move(String dir) { if(dir.equals("up")) { return new Seg(x,y-1); } else if(dir.equals("down")) { return new Seg(x,y+1); } else if(dir.equals("left")) { return new Seg(x-1,y); } else if(dir.equals("right")) { return new Seg(x+1,y); } else { return this; } } } // List of Segments interface ILoS { /** * Template * * Methods: * this.draw(Scene) -- Scene * this.move(dir) -- ILoS * this.dropLast() -- ILoS * this.onlyOne() -- boolean */ Scene draw(Scene s); ILoS move(String dir); ILoS dropLast(); boolean onlyOne(); } // A one segment list item class OneLoS implements ILoS { Seg first; OneLoS(Seg first) { this.first = first; } /** * Template * * Fields: * this.first -- Seg * Methods: * this.draw(Scene) -- Scene */ // Draws the only segment in the list public Scene draw(Scene s) { return first.draw(s); } // Moves the only segment public ILoS move(String dir) { return new OneLoS(first.move(dir)); } // This list has only 1 element public boolean onlyOne() { return true; } // Can not drop last, only one element public ILoS dropLast() { throw new Error("No last to drop"); } } // A cons list item for segments class ConsLoS implements ILoS { Seg first; ILoS rest; ConsLoS(Seg first, ILoS rest) { this.first = first; this.rest = rest; } /** * Template * * Fields: * this.first -- Seg * this.rest -- ILoS * Methods: * this.draw(Scene) -- Scene * this.dropLast() -- ILoS */ // Draws this segment with the rest of the segments public Scene draw(Scene s) { return rest.draw(first.draw(s)); } // Adds a moved segment and drops the last segment public ILoS move(String dir) { return new ConsLoS(first.move(dir),this.dropLast()); } // Removes the last segment from the list public ILoS dropLast() { if (rest.onlyOne()) { return new OneLoS(first); } else { return new ConsLoS(first,rest.dropLast()); } } // This list has more than 1 element public boolean onlyOne() { return false; } } class Snake { ILoS segs; String dir; Snake(ILoS segs, String dir) { super(); this.segs = segs; this.dir = dir; } /** * Template * * Fields: * this.segs -- ILoS * this.dir -- String * * Methods * this.draw(s) -- Scene * this.changeDir(String) -- Snake * this.move() -- Snake */ // Draws the snake segments Scene draw(Scene s) { return segs.draw(s); } // Sets a new direction for the snake Snake changeDir(String dir) { return new Snake(segs, dir); } // Moves the snake one step in current direction Snake move() { return new Snake(segs.move(dir),dir); } } // Examples and tests of our SnakeWorld objects class SnakeWorldExamples { Food f1 = new Food(3,3); Seg seg1 = new Seg(10,10); Seg seg2 = new Seg(10,11); Seg seg3 = new Seg(10,12); ILoS segs = new ConsLoS(seg1, new OneLoS(seg2)); Snake snake = new Snake(segs,"up"); SnakeWorld world = new SnakeWorld(snake,f1); World bang = world.bigBang(); boolean testCells(Tester t) { return(t.checkExpect(f1.cell2Pixel(),new Posn(70,70)) && t.checkExpect(seg1.cell2Pixel(),new Posn(90,110))); } }