// This file is part of www.nand2tetris.org // and the book "The Elements of Computing Systems" // by Nisan and Schocken, MIT Press. // File name: projects/11/Pong/Ball.jack /** * A graphical ball. Characterized by a screen location and distance of * last destination. Has methods for drawing, erasing and moving on the screen. * The ball is displayed as a filled, 6-by-6 pixles rectangle. */ class Ball { field int x, y; // the ball's screen location (in pixels) field int lengthx, lengthy; // distance of last destination (in pixels) field int d, straightD, diagonalD; // used for straight line movement computation field boolean invert, positivex, positivey; // (same) field int leftWall, rightWall, topWall, bottomWall; // wall locations field int wall; // last wall that the ball was bounced off of /** Constructs a new ball with the given initial location and wall locations. */ constructor Ball new(int Ax, int Ay, int AleftWall, int ArightWall, int AtopWall, int AbottomWall) { let x = Ax; let y = Ay; let leftWall = AleftWall; let rightWall = ArightWall - 6; // -6 for ball size let topWall = AtopWall; let bottomWall = AbottomWall - 6; // -6 for ball size let wall = 0; do show(); return this; } /** Deallocates the Ball's memory. */ method void dispose() { do Memory.deAlloc(this); return; } /** Shows the ball. */ method void show() { do Screen.setColor(true); do draw(); return; } /** Hides the ball. */ method void hide() { do Screen.setColor(false); do draw(); return; } /** Draws the ball. */ method void draw() { do Screen.drawRectangle(x, y, x + 5, y + 5); return; } /** Returns the ball's left edge. */ method int getLeft() { return x; } /** Returns the ball's right edge. */ method int getRight() { return x + 5; } /** Computes and sets the ball's destination. */ method void setDestination(int destx, int desty) { var int dx, dy, temp; let lengthx = destx - x; let lengthy = desty - y; let dx = Math.abs(lengthx); let dy = Math.abs(lengthy); let invert = (dx < dy); if (invert) { let temp = dx; // swap dx, dy let dx = dy; let dy = temp; let positivex = (y < desty); let positivey = (x < destx); } else { let positivex = (x < destx); let positivey = (y < desty); } let d = (2 * dy) - dx; let straightD = 2 * dy; let diagonalD = 2 * (dy - dx); return; } /** * Moves the ball one unit towards its destination. * If the ball has reached a wall, returns 0. * Else, returns a value according to the wall: * 1 (left wall), 2 (right wall), 3 (top wall), 4 (bottom wall). */ method int move() { do hide(); if (d < 0) { let d = d + straightD; } else { let d = d + diagonalD; if (positivey) { if (invert) { let x = x + 4; } else { let y = y + 4; } } else { if (invert) { let x = x - 4; } else { let y = y - 4; } } } if (positivex) { if (invert) { let y = y + 4; } else { let x = x + 4; } } else { if (invert) { let y = y - 4; } else { let x = x - 4; } } if (~(x > leftWall)) { let wall = 1; let x = leftWall; } if (~(x < rightWall)) { let wall = 2; let x = rightWall; } if (~(y > topWall)) { let wall = 3; let y = topWall; } if (~(y < bottomWall)) { let wall = 4; let y = bottomWall; } do show(); return wall; } /** * Bounces off the current wall: sets the new destination * of the ball according to the ball's angle and the given * bouncing direction (-1/0/1=left/center/right or up/center/down). */ method void bounce(int bouncingDirection) { var int newx, newy, divLengthx, divLengthy, factor; // dividing by 10 first since results are too big let divLengthx = lengthx / 10; let divLengthy = lengthy / 10; if (bouncingDirection = 0) { let factor = 10; } else { if (((~(lengthx < 0)) & (bouncingDirection = 1)) | ((lengthx < 0) & (bouncingDirection = (-1)))) { let factor = 20; // bounce direction is in ball direction } else { let factor = 5; } // bounce direction is against ball direction } if (wall = 1) { let newx = 506; let newy = (divLengthy * (-50)) / divLengthx; let newy = y + (newy * factor); } else { if (wall = 2) { let newx = 0; let newy = (divLengthy * 50) / divLengthx; let newy = y + (newy * factor); } else { if (wall = 3) { let newy = 250; let newx = (divLengthx * (-25)) / divLengthy; let newx = x + (newx * factor); } else { // assumes wall = 4 let newy = 0; let newx = (divLengthx * 25) / divLengthy; let newx = x + (newx * factor); } } } do setDestination(newx, newy); return; } }