Java代写:CS2510 Forbidden Island

用Java Swing绘制一个随机的荒岛地图。

Goals

Design a game with mutable world state, ArrayLists, mutable linked data structures, and loops.

You will be using the Impworld library, as in Lab 9 - make sure that at the top of your file, you include

1
2
3
4
5
import java.util.ArrayList;
import tester.*;
import javalib.impworld.*;
import java.awt.Color;
import javalib.worldimages.*;

You will submit this project twice. For Part 1 you must have completed the task of Creating the island and Rendering the game (see below) for at least the first two islands (the regular mountain and the diamond island of random heights).

For Part 2 you must complete all of the tasks, so Creating the island and Rendering the game plus Flooding the island, Gameplay and Resetting the game.

Setting the scene

You awaken alone, marooned on an island. You cannot recall how you got here, but desperately want to get back home. Around you are scattered pieces of machinery, which you suspect are important somehow. Looking up to the peak of the island, you see a somewhat ruined helicopter. How convenient that you are a trained pilot - and mechanic! You notice that the tide is rushing in: soon, the island will be flooded and the helicopter will be underwater. Can you collect all the pieces of the helicopter and fly away before it’s too late?

Tasks

Here is a list of tasks you will need to complete to implement this game.

Creating the island

Here are three island geometries that you will need to produce:

  • A perfectly regular mountain:
  • A diamond island of random heights:
  • A randomly-generated terrain:

Your primary data structures will be the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Represents a single square of the game area
class Cell {
// represents absolute height of this cell, in feet
double height;
// In logical coordinates, with the origin at the top-left corner of the screen
int x, y;
// the four adjacent cells to this one
Cell left, top, right, bottom;
// reports whether this cell is flooded or not
boolean isFlooded;
}
class OceanCell extends Cell {
}

class ForbiddenIslandWorld extends World {
// All the cells of the game, including the ocean
IList<Cell> board;
// the current height of the ocean
int waterHeight;
}

You will need to create a two-dimensional grid of these Cells to represent the island and its surrounding ocean. Ocean cells should be instances of the OceanCell class, and will be rendered differently than regular Cells.

Since the size of the game should be easily configurable, you should make your code dependent on a single constant, which can be changed easily to resize your island. To define constants in Java, use the following syntax:

We have not discussed the keywords static or final in this course, but you will learn more about them in OOD.

1
2
3
4
5
class ForbiddenIslandWorld extends World {
// Defines an int constant
static final int ISLAND_SIZE = 64;
...
}

You can then refer to your constant as ForbiddenIslandWorld.ISLAND_SIZE from anywhere in your program.
Because the size of your island is determined by this constant, you cannot simply hard-code lists of data, because they may be of the wrong size. Instead, you’ll need to use loops and ArrayLists.

The “Manhattan distance” is so named because in a gridded city like Manhattan, the shortest path from one point to another must follow the streets and avenues, rather than take the diagonal straight-line path (which would correspond to the Euclidean distance, using the Pythagorean theorem).

To create a mountain island, the height of a cell should be equal to the total height of the island minus its “Manhattan distance” from the center of the board (i.e., the sum of its x-distance and its y-distance from the center). Cells at a far-enough Manhattan distance are part of the ocean. In the screenshot above, the ocean is 32 cells away from the center of the island.

The easiest way to construct this mountain is to create an ArrayList of Double, representing the heights of every cell in the game. You’ll need to use nested counted-for loops to initialize the lists properly. Then, you should create an ArrayList of Cell, where each Cell’s height is determined by the corresponding item in the previous list of lists of integers. (Why the extra step? Because once you have created an ArrayList of Double, for any of the three island shapes, converting from that to Cells is common to all three shapes.) Finally, once you have created all the Cells, be sure to fix up their top/bottom/left/right neighbor links appropriately. For Cells on the border of the game, you can set their neighbor to be themselves (creating a cycle, and effectively making the ocean be infinitely large in that direction).

To create a random island, again the ocean is at a Manhattan distance of 32 away from the center, but every cell’s height is randomly assigned.

The random terrain island is a bit trickier to construct. Again the island’s heights are randomly selected, but there are relationships between the heights of adjacent cells. This algorithm is often called a subdivision algorithm, since it divides the grid into quarters at each step. The idea is to construct the height of the inside of a rectangle of cells, given only the heights of the corners of the rectangle. So for the schematic below, if we are given heights tl, tr, bl and br,

tl ----------- tr
 |             |
 |             |
 |             |
bl ----------- br

We can construct heights for the midpoints of each edge:

tl ---- t ---- tr
 |             |
 l             r
 |             |
bl ---- b ---- br

using the following formulas:

  • t=random()+(tl+tr)/2
  • b=random()+(bl+br)/2
  • l=random()+(tl+bl)/2
  • r=random()+(tr+br)/2

Finally, we can construct the height for the middle of the whole rectangle:

tl ---- t ---- tr
 |      |      |
 l ---- m ---- r
 |      |      |
bl ---- b ---- br

using the formula

  • m=random()+(tl+tr+bl+br)/4

Each item is the average of the items surrounding it, plus a random nudge. For added realism, the size of the random nudge should be scaled so that it’s proportional to the area of the rectangle.

This procedure creates four smaller rectangles, which we can then recursively process to create the rest of the island’s heights.

To set this whole process in motion,

  • Initialize your ArrayList of Double to contain ISLAND_SIZE + 1 rows of ISLAND_SIZE + 1 columns of zeros.
  • Set the values of the four corners of your ArrayList of Double to zero (so that the corners are under water).
  • Initialize the center of your grid to the maximum height of your island.
  • Initialize the middles of the four edges to height 1 (so they are just above water).

Then call the procedure above for each of the four quadrants of the game board.

(Hint: you’re going to have to work carefully with array indices here, and in the recursive calls. Think about how we implemented binary-search over ArrayLists for inspiration on how to handle the indices here.) This will generate a pleasingly random terrain that is bumpier than the perfect mountain, but less erratic than the fully random board. It might also generate “lakes” of cells that are underwater. You should treat any such lakes as OceanCells.

If you implement some of the extra-credit suggestions below, this paragraph is no longer 100% true…

When you have finished creating your ArrayList of Cell, the cells themselves will never change (the board does not change size during the game, but the cells can be modified to indicate their flooded status). So collect all the cells into a single IList of Cell that is stored in the world. USe IList here so that if you want to add methods to it, you can do so.

Rendering the game

All water renders as blue.

Cells that are above water should be rendered from green (meaning just barely above water) to white (meaning quite high).

Cells that are flooded should be rendered from blue (meaning just below the water) to black (meaning quite submerged).

Cells that are below the current water level, but that are not flooded, should be rendered on a scale from green (meaning just below the water level) to red (meaning dangerously prone to flooding).

Here is a sequence of images showing the random island above before, during, and after it has flooded, to give you a sense of what this might look like:

Flooding the island

This algorithm, appropriately enough, is called the flood-fill algorithm, and is the essential algorithm used in graphics applications to fill a contiguous area with color.

Every ten ticks of the game, the water level should rise by one foot. When the water rises, you must flood any Cells that (1) were not yet flooded, (2) were adjacent to the water, and (3) are now below the water level. All the cells that can flood, should flood on the same tick. To do this efficiently, you’ll likely need a function that computes the list of Cells that form the current coastline of the island (again, any lakes inside the island count towards the coastline). As you flood cells, update their isFlooded flag to true, to indicate that they are now flooded (this will affect how the cells are displayed).

Gameplay

Player mechanics: Place the player somewhere at random on the island. Render the player onto the display. You can choose any simple picture to represent the player, such as this pilot , or even just a simple circle or star shape. Use the arrow keys to move the player - but prevent the player from moving onto flooded cells. If the player stays still long enough, and the water rises enough to flood the cell the player is standing on, the game is over.

Goals: Distribute some number of helicopter pieces randomly around the island. Draw the pieces as simple circles. The player must collect all the pieces and get to the highest point on the island where the crashed helicopter landed. Draw the helicopter using this image: . (Hint: you probably will want to define a Target class to represent everything that the player needs to pick up, a HelicopterTarget subclass of Target that represents the final helicopter target and that can only be “picked up” after all the other targets have been picked up, and maintain a list of Targets still remaining in the game. Targets can be picked up simply by having the player walk over them, and should then be removed from the game.)

Resetting the game

Use the ‘m’, ‘r’, and ‘t’ keys to reset the game and create a new mountain, random, or terrain island.

Extra credit

If you want to earn extra credit on this assignment, you can complete any number of “whistles” and “bells”. A “whistle” is a fairly small extension to the game; a “bell” is a more elaborate and impressive enhancement. It would take several whistles to be as impressive as a single bell. Whistles and bells are not worth a specific number of extra credit points; they are subjectively graded, and will count toward improving your exam scores if needed. (You should therefore aim to implement features that demonstrate that you have mastered the concepts that you got wrong on your exams!)

Whistles and bells will only count towards extra credit if they are convincingly and thoroughly tested. Moreover, the rest of the game must be at least as well tested - you will not receive extra credit if the required parts of the game are not designed properly or do not work properly.

Here are some examples of whistles and bells:

Whistles

  • Enhancing the graphics. (Very small whistle!)

  • Keeping score: how many steps does the player take before reaching the goal? Lower scores are better… You’d need to enhance the display to render the score so far somehow.

  • Or, keeping time: display a countdown timer until the island floods completely.

Bells

  • Scuba: add an underwater swimming suit somewhere on the island. If the player finds it, and activates it (with ‘s’ for “swim”), allow them to swim through flooded cells for a limited window of time.

  • Engineer: allow the player to (press ‘b’ and) rebuild a flooded 5-by-5-cell region of the board, and raise it up 5 feet above the current water level.

  • Earthquake: at random, an earthquake can strike and trigger a landslide. In a landslide, find any cells that are more than 4 units higher than at least one of their neighbors. Have that cell lose 4 units of height, and distribute those lost 4 units of height evenly among all the neighbors that are shorter than it (i.e., a landslide can’t cause land to slide uphill!). Of course, once one cell slides, it may cause other cells to be more than 4 units taller than their neighbors, so repeat this procedure until everything stabilizes. NOTE: if a cell on the coastline is taller than 4 units, it can convert its neighbors from OceanCells to normal Cells, and you must carefully fix up all the links between cells.

  • Multiplayer: allow two players to search the island together. (Have the second player use ‘a’, ‘s’, ‘d’, ‘w’ to move.) Both players must make it to the helicopter to win the game.

  • Hard! (But very cool) Different island connectivity: You’ve implemented the island as a connected grid of squares. Try implementing the island as a connected grid of hexagons. To do this, you’d need:

    • To figure out how to render a hexagon. See The Image Library.
    • To figure out how to represent a hexagonal grid. You’ll need to update your Cell class to have six neighbors instead of four. You’ll also need to figure out how to represent a hexagonal grid using a normal ArrayList of Cell. (Hint: if you look at the rows of a hexagonal grid, every other row is “shifted” by half a cell-width, but there are the same number of cells in every row, so a regular ArrayList of Cell should still work.)
    • To figure out how to render a hexagonal grid. You’ll need a little bit of math to figure out the centers of each hexagon.
    • To figure out how to do terrain generation. Your code above will continue to work (since it operates over ArrayList of Double data), but will produce slightly strange-looking output. The analogue of generating terrain here is to use triangles, and to compute the midpoints of the sides…

To figure out user-input controls. I suggest using the letter ‘a’, ‘w’, ‘e’, ‘d’, ‘x’ and ‘z’, to mean their “obvious” directions (relative to the letter ‘s’ on the keyboard). If you also implement two-player mode, you’ll need to come up with different letters for that player and/or the island-resetting letters.

You are encouraged and welcome to think of other enhancements to the game; talk them over with the professors to determine if they are whistles or bells. Have fun!