package c3Oceansimple; import tester.*; import javalib.funworld.*; import javalib.colors.*; import javalib.worldcanvas.WorldCanvas; import javalib.worldimages.*; import java.util.*; import java.awt.Color; import c2Oceanimages.ExamplesOceanImages; // the basic data used by the ocean world game interface OceanWorldConstants{ // a random number generator public Random rand = new Random(); // the width of the world public int WIDTH = 400; // the height of the world public int HEIGHT = 400; // the color of the ocean public Color oceanColor = new Color(50, 150, 255); // the background image of the ocean public WorldImage oceanImage = new RectangleImage(new Posn(WIDTH/2, HEIGHT/2), WIDTH, HEIGHT, oceanColor); } //To represent a location (x,y) in graphics coordinates //-- adding methods to the library Posn class class CartPt extends Posn implements OceanWorldConstants{ // the standard constructor - invokes the one in the super class CartPt(int x, int y) { super(x, y); } /** TEMPLATE Fields: ... this.x ... -- int ... this.y ... -- int Methods: ... this.moveLeft(int) ... -- CartPt ... this.moveLeftRandom(int) ... -- CartPt ... this.distTo(CartPt) ... -- double */ // Move this CartPt left i units -- or back to the right, if out of bounds CartPt moveLeft(int i) { if (this.x - i < 0) return new CartPt(WIDTH, this.y); else return new CartPt(this.x-i, this.y); } // Move this CartPt left i units -- or back to the right, if out of bounds // also move randomly up or down -2 to 2 pixel CartPt moveLeftRandom(int i) { if (this.x - i < 0) return new CartPt(WIDTH, this.y); else // our tests did not catch that rand.nextInt(2) did not provide all desired values return new CartPt(this.x-i, this.y + rand.nextInt(5) - 2); } // Compute the distance from this point to the given one double distTo(CartPt that){ return Math.sqrt((this.x - that.x) * (this.x - that.x) + (this.y - that.y) * (this.y - that.y)); } } // A Shark swims in the ocean, looking for fish to eat class Shark implements OceanWorldConstants{ int y; // the horizontal coordinate int health; // the hunger level, when zero, the shark dies // the standard complete constructor Shark(int y, int health) { this.y = y; this.health = health; } /** TEMPLATE Fields: ... this.y ... -- int ... this.health ... -- int Methods: ... this.move(int) ... -- CartPt ... this.sharkImage() ... -- WorldImage ... this.starve() ... -- Shark ... this.isDead() ... -- boolean ... this.eat(int) ... -- Shark */ // produce the position of this shark CartPt sharkPos(){ return new CartPt(20, this.y); } // move the shark up or down, on up-down key press Shark move(String ke){ if (ke.equals("up")) return new Shark(this.y - 3, this.health); else if (ke.equals("down")) return new Shark(this.y + 3, this.health); else return this; } // produce the image of this shark at its position WorldImage sharkImage(){ return new FromFileImage(this.sharkPos(), "small-shark.png"); } // produce a shark hungrier than before Shark starve(){ return new Shark(this.y, this.health - 1); } // is this shark dead? boolean isDead(){ return this.health <= 0; } // produce a fatter shark after he ate the fish of the given size Shark eat(int size){ return new Shark(this.y, this.health + size); } } // A Fish swims in the ocean - providing nourishment for the shark class Fish implements OceanWorldConstants{ CartPt p; // the location of this fish int size; // the size of this fish // the standard complete constructor Fish(CartPt p, int size) { this.p = p; this.size = size; } /** TEMPLATE Fields: ... this.p ... -- CartPt ... this.size ... -- int Methods: ... this.start(int, int) ... -- Fish ... this.swim() ... -- Fish ... this.closeByHuh(Shark) ... -- boolean ... this.fishImage() ... -- WorldImage */ // produce a fish at the right side of the ocean Fish start(int y, int size){ return new Fish(new CartPt(WIDTH, y), size); } // fish swims from right to left towards the waiting shark Fish swim() { return new Fish(this.p.moveLeftRandom(5), this.size); } // Is this fish close to the given shark? boolean closeByHuh(Shark that) { return this.p.distTo(that.sharkPos()) < 30; } // produce the image of this fish at its position WorldImage fishImage(){ return new FromFileImage(this.p, "small-green-fish.png"); } } // A School of Fish is one of: // - new EmptySchool() // - new ConsSchool(Fish, School) // A school of (an arbitrary number of) fish. interface School extends OceanWorldConstants{ public WorldImage fishesImage(); } // A school with no fish class EmptySchool implements School, OceanWorldConstants { EmptySchool() {} // produce an empty blue dot in the sea of blue public WorldImage fishesImage(){ return new CircleImage(new Posn(0, 0), 1, new Blue()); } } // A school with at least one fish class ConsSchool implements School, OceanWorldConstants { Fish first; School rest; // the standard complete constructor ConsSchool (Fish first, School rest) { this.first = first; this.rest = rest; } // produce the image of this school of fish public WorldImage fishesImage(){ return new OverlayImages(this.first.fishImage(), this.rest.fishesImage()); } } //An Ocean has a shark and a fish swimming in it class Ocean extends World implements OceanWorldConstants{ Shark shark; // the hungry shark Fish fish; // the fish the shark will eat (maybe) Ocean(Shark shark, Fish fish) { this.shark = shark; this.fish = fish; } /** TEMPLATE Fields: ... this.shark ... -- Shark ... this.fish ... -- Fish Methods: ... this.onTick() ... -- Ocean ... this.onKeyEvent(String) ... -- Ocean ... this.makeImage() ... -- WorldImage ... this.worldEnds() ... -- WorldEnd */ // the fish swim from right to left on each tick public World onTick(){ if (this.fish.closeByHuh(this.shark)){ return new Ocean(this.shark.eat(this.fish.size), this.fish.start(this.fish.p.y, this.fish.size)); } else return new Ocean(this.shark.starve(), this.fish.swim()); } // the shark moves up or down as directed by the arrow key public World onKeyEvent(String ke){ return new Ocean(this.shark.move(ke), this.fish); } // produce the image of the fish and shark swimming in the sea of blue public WorldImage makeImage(){ return oceanImage.overlayImages(this.shark.sharkImage(), this.fish.fishImage()); } public WorldEnd worldEnds(){ if (this.shark.isDead()) return new WorldEnd(true, new OverlayImages(this.makeImage(), new TextImage(new Posn(100, 50), "The shark died", new Red()))); else return new WorldEnd(false, this.makeImage()); } } // Examples for the ocean game public class ExamplesOceanSimple implements OceanWorldConstants{ ExamplesOceanSimple() {} public static ExamplesOceanSimple examplesInstance = new ExamplesOceanSimple(); CartPt p1 = new CartPt(WIDTH, HEIGHT/2); CartPt pEnd = new CartPt(3, HEIGHT/2); CartPt pStart = new CartPt(WIDTH, HEIGHT/2); Fish f = new Fish(p1, 50); Shark s = new Shark(HEIGHT/2, 200); Ocean ocean = new Ocean(this.s, this.f); // test all methods in the class Shark public boolean testSharkMethods(Tester t){ return t.checkExpect(this.s.sharkPos(), new CartPt(20, HEIGHT/2)) && t.checkExpect(this.s.move("up"), new Shark(HEIGHT/2 - 3, 200)) && t.checkExpect(this.s.move("down"), new Shark(HEIGHT/2 + 3, 200)) && t.checkExpect(this.s.move("left"), new Shark(HEIGHT/2, 200)) && t.checkExpect(this.s.starve(), new Shark(HEIGHT/2, 199)) && t.checkExpect(this.s.isDead(), false) && t.checkExpect(new Shark(30, 0).isDead(), true) && t.checkExpect(this.s.eat(20), new Shark(HEIGHT/2, 220)); } // test all methods in the class Fish public boolean testFishMethods(Tester t){ return t.checkExpect(this.f.start(30, 50), new Fish(new CartPt(WIDTH, 30), 50)) && //t.checkExpect(this.f.swim(), new Fish(new CartPt(195, 100), 50)) && t.checkExpect(new Fish(new CartPt(2, HEIGHT/2), 50).swim(), new Fish(new CartPt(WIDTH, HEIGHT/2), 50)) && t.checkExpect(this.f.closeByHuh(this.s), false) && t.checkExpect(new Fish(new CartPt(19, 195), 30).closeByHuh(this.s), true); } // test all methods in the class CartPt public boolean testCartPtMethods(Tester t) { return t.checkExpect(this.p1.moveLeft(5), new CartPt(395,HEIGHT/2)) && t.checkExpect(this.pEnd.moveLeft(5), this.pStart) && t.checkExpect(this.p1.distTo(this.pEnd), 397.0); } // illustrates the tests when the result is one of possible values // or within some range public void testRandomMethods(Tester t){ for (int i = 0; i < 100; i++){ t.checkRange((new CartPt(10, 20).moveLeftRandom(5)).y, 18, 23); } for (int i = 0; i < 100; i++){ t.checkOneOf(new Fish(new CartPt(10, 20), 25).swim(), new Fish(new CartPt(5, 18), 25), new Fish(new CartPt(5, 19), 25), new Fish(new CartPt(5, 20), 25), new Fish(new CartPt(5, 21), 25), new Fish(new CartPt(5, 22), 25)); } } // test the method onKeyEvent for the Ocean class public void testOnKeyEvent(Tester t){ t.checkExpect(this.ocean.onKeyEvent("x"), this.ocean); t.checkExpect(this.ocean.onKeyEvent("up"), new Ocean(new Shark(HEIGHT/2 - 3, 200), this.f)); t.checkExpect(this.ocean.onKeyEvent("down"), new Ocean(new Shark(HEIGHT/2 + 3, 200), this.f)); } Fish sharkFood = new Fish(new CartPt(3, HEIGHT/2 + 3), 20); Fish outOfBoundsFish = new Fish(new CartPt(3, 100), 20); Ocean sharkEats = new Ocean(this.s, this.sharkFood); Ocean fishEscapes = new Ocean(this.s, this.outOfBoundsFish); // tests for the onTick methods should be done // before we introduce the random behavior // assume the fish just swims left 3 pixels public void testOnTick(Tester t){ t.checkExpect(this.ocean.onTick(), new Ocean(new Shark(HEIGHT/2, 199), new Fish(new CartPt(WIDTH - 3, HEIGHT/2), 50))); t.checkExpect(this.ocean.onTick(), new Ocean(new Shark(HEIGHT/2, 199), new Fish(new CartPt(WIDTH - 3, HEIGHT/2), 50))); t.checkExpect(this.sharkEats.onTick(), new Ocean(new Shark(HEIGHT/2, 220), new Fish(new CartPt(WIDTH, HEIGHT/2 + 3), 20))); t.checkExpect(this.fishEscapes.onTick(), new Ocean(new Shark(HEIGHT/2, 199), new Fish(new CartPt(WIDTH, 100), 20))); } // tests for the method worldEnds public void testWorldEnds(Tester t){ t.checkExpect(this.ocean.worldEnds(), new WorldEnd(false, this.ocean.makeImage())); t.checkExpect((new Ocean(new Shark(200, 0), this.f)).worldEnds(), new WorldEnd(true, new OverlayImages((new Ocean(new Shark(200, 0), this.f)).makeImage(), new TextImage(new Posn(100, 50), "The shark died", new Red())))); } // test the drawing of the ocean world image public void testDrawing(Tester t){ WorldCanvas canvas = new WorldCanvas(WIDTH, HEIGHT); canvas.drawImage(this.ocean.makeImage()); canvas.show(); } // run the game public void testRun(Tester t){ this.ocean.bigBang(400, 400, 0.07); } // run all tests, then run the game public static void main(String[] argv){ ExamplesOceanSimple e = new ExamplesOceanSimple(); Tester.runReport(e, false, false); e.ocean.bigBang(400, 400, 0.1); } }