Homework 13 - james-bern/CS136 GitHub Wiki

The final project is effectively an open-ended Homework.

  • You can use Cow.java (✨ grab a fresh copy!--it has a bugfix you may need) or any of the past homework, but you do not have to.
  • You must get permission beforehand if you want to use a language other than Java.
  • You must get permission beforehand if you want to use any external code other than past homeworks and Cow.java.
  • You must get permission beforehand if you want to work with a partner.
    • Note: Your project will have to be twice as good because there are two of you. 🙂👍

You may do your final project on whatever you like, provided you can answer the following questions.

  1. What is the title of my project?
  2. What data structures will I use? Note: Arrays count.
  3. What is the game/app that I am proposing? Who or what lives in it? What does it do? How does it feel?
  4. Will the viewer/player interact with my project? How so?
  5. Does Jim think my project is doable? What is my fallback plan if my project ends up being harder/more time-consuming than I expect? What extensions can I do if my project ends up being easier/less time-consuming than I expect?
  6. What is the very first thing I will implement? (Drawing "the data" is usually a good first step.)

Here is a great example:

  1. Woo!-doku
  2. 2D array of ints to represent the board. (int[][] board = new int[9][9];)
  3. A color sudoku board, that does a happy dance when you solve it.
  4. Click to select cells. Type numbers on the keyboard to fill in numbers.
  5. Yes! And you can write a sudoku solver or automatic board generation if you have extra time!
  6. Store a board I found on the internet as a 2D array (-1's for empty cells) and draw it to the Terminal using System.out.println.

Example Programs

Basic Setup

This sets up Cow to have the lower left corner of the window be $(0, 0)$ and the upper right corner of the window be $(N, N)$. For grid-based games this can be very useful!

👀
class HW13 extends App {
    // NxN grid
    // each grid cell is 1 unit long (see main())
    static int N = 16;

    void setup() {

    }

    void loop() {

    }

    public static void main(String[] arguments) {
        App app = new HW13();
        app.setWindowBackgroundColor(Vector3.white);

        // // the units of your game
        // make world an N x N square
        app.setWindowSizeInWorldUnits(N, N);
        // put world origin in lower left corner of the screen
        app.setWindowCenterInWorldUnits(N / 2.0, N / 2.0);

        // // NOT the units of your game
        // how much of your screen the window takes up
        app.setWindowHeightInPixels(512);
        // where the window spawns on your screen
        app.setWindowTopLeftCornerInPixels(16, 16);

        app.run();
    }
}

Mouse in Rectangle

Here we ask if the mouse position is inside a rectangle.

👀
class HW13 extends App {
    static int N = 16;

    void setup() {

    }

    void loop() {
        Vector2 lowerLeftCorner = new Vector2(0.5, 0.5);
        Vector2 upperRightCorner = new Vector2(1.7, 2.8);
        drawCornerRectangle(lowerLeftCorner, upperRightCorner, Vector3.blue);

        boolean isMouseInsideOfTheRectangle =
            (lowerLeftCorner.x < mousePosition.x) && (mousePosition.x < upperRightCorner.x)
            && (lowerLeftCorner.y < mousePosition.y) && (mousePosition.y < upperRightCorner.y);

        Vector3 circleColor;
        if (isMouseInsideOfTheRectangle) {
            circleColor = Vector3.red;
        } else {
            circleColor = Vector3.green;
        }

        drawCircle(mousePosition, 0.1, circleColor);
    }

    public static void main(String[] arguments) {
        App app = new HW13();
        app.setWindowBackgroundColor(Vector3.white);
        app.setWindowSizeInWorldUnits(N, N);
        app.setWindowCenterInWorldUnits(N / 2.0, N / 2.0);
        app.setWindowHeightInPixels(512);
        app.setWindowTopLeftCornerInPixels(16, 16);
        app.run();
    }
}
image

Mouse in Grid

Here we draw a grid and highlight the cell that the mouse is inside of.

Because the grid is an integer grid (the corners of the cells are $(0, 0), (1, 0), (0, 1), (2, 0), (1, 1), (0, 2), ...$), we can find the lower left corner of the current cell by casting the x and y coordinates of the mouse's position to int'ss. The upper right corner is the lower left corner plus $(1, 1)$.

👀
class HW13 extends App {
    static int N = 16;

    void setup() {

    }

    void loop() {
        // highlight square
        int x = (int) mousePosition.x;
        int y = (int) mousePosition.y;
        drawCornerRectangle(new Vector2(x, y), new Vector2(x + 1, y + 1), Vector3.yellow);

        // draw grid
        for (int i = 0; i <= N; ++i) {
            drawLine(new Vector2(0.0, i), new Vector2(N, i), Vector3.black);
            drawLine(new Vector2(i, 0.0), new Vector2(i, N), Vector3.black);
        }
    }

    public static void main(String[] arguments) {
        App app = new HW13();
        app.setWindowBackgroundColor(Vector3.white);
        app.setWindowSizeInWorldUnits(N, N);
        app.setWindowCenterInWorldUnits(N / 2.0, N / 2.0);
        app.setWindowHeightInPixels(512);
        app.setWindowTopLeftCornerInPixels(16, 16);
        app.run();
    }
}

Projectile Motion

Here we simulate the physics of a cannonball.

If the position of the ball is $\mathbf{s} = (x, y)^T$, the velocity is $\mathbf{v} = (v_x, v_y)^T$, and the acceleration is $\mathbf{a} = (0, -1)^T$, then the explicit euler update rule with timestep $h$ is

$$\mathbf{s}_{k+1} = \mathbf{s}_k + h * \mathbf{v}_k$$

$$\mathbf{v}_{k+1} = \mathbf{v}_k + h * \mathbf{a}_k$$

Note: If we consider the state to be $(\mathbf{s}, \mathbf{v})^T$, then the update rule is just $\mathbf{\xi}_{k+1} = \mathbf{\xi}_k + h * \dot{\mathbf{\xi}}_k$, where $\dot{\mathbf{\xi}} = \frac{d\mathbf{\xi}}{dt}.$

👀
class HW13 extends App {
    static int N = 16;

    Vector2 s; // position
    Vector2 v; // velocity
    Vector2 a; // acceleration
    double h;  // timestep ("delta t")

    void setup() {
        s = new Vector2(0.0, 0.0);
        v = new Vector2(2.0, 4.0);
        a = new Vector2(0.0, -1.0);
        h = 0.1;
    }

    void loop() {
        // explicit euler update
        v = v.plus(a.times(h)); // v += h * a;
        s = s.plus(v.times(h)); // s += h * v;
        drawCircle(s, 0.2, Vector3.black);
    }

    public static void main(String[] arguments) {
        App app = new HW13();
        app.setWindowBackgroundColor(Vector3.white);
        app.setWindowSizeInWorldUnits(N, N);
        app.setWindowCenterInWorldUnits(N / 2.0, N / 2.0);
        app.setWindowHeightInPixels(512);
        app.setWindowTopLeftCornerInPixels(16, 16);
        app.run();
    }
}

Gradient

Here we store the color of each cell in the $N\times N$ grid. In setup(), we map the row and column of each cell to a double between $0$ and $1$, and then use the Vector3 static method rainbowSwirl to turn that double into a color.

👀
class HW13 extends App {
    static int N = 16;

    Vector3 gradient[][];

    void setup() {
        gradient = new Vector3[N][N];
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                double t = (double) (N * j + i) / (N * N);
                gradient[i][j] = Vector3.rainbowSwirl(t);
            }
        }
    }

    void loop() {
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                // NOTE: We have to cast the numerator to a double, otherwise an int divided by an int is an int!
                double t = (double) (N * i + j) / (N * N);
                drawCornerRectangle(new Vector2(i, j), new Vector2(i + 1, j + 1), gradient[i][j]);
            }
        }
    }

    public static void main(String[] arguments) {
        App app = new HW13();
        app.setWindowBackgroundColor(Vector3.white);
        app.setWindowSizeInWorldUnits(N, N);
        app.setWindowCenterInWorldUnits(N / 2.0, N / 2.0);
        app.setWindowHeightInPixels(512);
        app.setWindowTopLeftCornerInPixels(16, 16);
        app.run();
    }
}
⚠️ **GitHub.com Fallback** ⚠️