这次回顾第九章的内容,这一章介绍了Jack编程语言。

课程官网:

https://www.nand2tetris.org/

视频地址:

https://www.coursera.org/learn/build-a-computer

Part 1:课程回顾

背景介绍

这章介绍了Jack语言,项目为利用Jack语言写一个app,背景介绍部分从略。

Part 2:项目

这部分编写了Breakout游戏,截图如下:

该程序的构成如下:

Ball

/** BALL */

class Ball{
	
	// screen location of the ball's center
	field int x, y;
	// radius of the ball
	field int r;
	// x direction of the ball, 0 means right, 1 means left
	field int directionx;
	// y direction of the ball, 0 means down, 1 means up
	field int directiony;

	/** Constructs a new ball. */
	constructor Ball new(int Ax, int Ay, int Ar) {
		let directionx = 0;
		let directiony = 0;
		let x = Ax;
		let y = Ay;
		let r = Ar;
		do draw();
		return this;
	}
	
	/** Draw thet ball */
	method void draw() {
		do Screen.setColor(true);
		do Screen.drawCircle(x, y, r);
		return;
	}
	
	/** Moves the ball left and up by 1 pixels. */
	method void moveUpLeft(){
		if (((x - r) > 1) & ((y - r) > 1)) {
			do Screen.setColor(false);
			do Screen.drawCircle(x, y, r);
			let x = x - 1;
			let y = y - 1;
			do Screen.setColor(true);
			do Screen.drawCircle(x, y, r);
		}
		return;
	}
	
	/** Moves the ball left and down by 1 pixels. */
	method void moveDownLeft(){
		if (((x - r) > 1) & ((y + r) < 255)) {
			do Screen.setColor(false);
			do Screen.drawCircle(x, y, r);
			let x = x - 1;
			let y = y + 1;
			do Screen.setColor(true);
			do Screen.drawCircle(x, y, r);
		}
		return;
	}
	
	/** Moves the ball right and up by 1 pixels. */
	method void moveUpRight() {
		if (((x + r) < 511) & ((y - r) > 1)) {
			do Screen.setColor(false);
			do Screen.drawCircle(x, y, r);
			let x = x + 1;
			let y = y - 1;
			do Screen.setColor(true);
			do Screen.drawCircle(x, y, r);
		}
		return;
	}
	
	/** Moves the ball right and down by 1 pixels. */
	method void moveDownRight() {
		if (((x + r) < 511) & ((y + r) < 255)) {
			do Screen.setColor(false);
			do Screen.drawCircle(x, y, r);
			let x = x + 1;
			let y = y + 1;
			do Screen.setColor(true);
			do Screen.drawCircle(x, y, r);
		}
		return;
	}
	
	/** Erase the ball. */
	method void erase() {
		do Screen.setColor(false);
		do Screen.drawCircle(x, y, r);
		return;
	}
	
	/** Return radius. */
	method int r() {
		return r;
	}
	
	/** Return x. */
	method int x() {
		return x;
	}
	
	/** Return y. */
	method int y() {
		return y;
	}
	
	/** Return directionx. */
	method int directionx() {
		return directionx;
	}
	
	/** Return directiony. */
	method int directiony() {
		return directiony;
	}
	
	/** Set directionx. */
	method void set_directionx(int x){
		let directionx = x;
		return;
	}
	
	/** Set directiony. */
	method void set_directiony(int y){
		let directiony = y;
		return;
	}
}

Brick

/** Brick. */

class Brick {
	
	// screen location of the square's top-left corner
	field int x, y; 
	// width of the brick
    field int width; 
	// height of the brick
	field int height;
	// judge whether to show the brick
	field int state;
	
	/** Constructs a new Bricks with a given location and size. */
	constructor Brick new(int Ax, int Ay, int w, int h) {
		let x = Ax;
		let y = Ay;
		let width = w;
		let height = h;
		let state = 1;
		do draw();
		return this;
	}
	
	/** Draws the brick on the screen. */
	method void draw() {
		do Screen.setColor(true);
		do Screen.drawRectangle(x, y, x + width, y + height);
		return;
	}
	
	/** Erases the brick from the screen. */
	method void erase() {
		do Screen.setColor(false);
		do Screen.drawRectangle(x, y, x + width, y + height);
		return;
	}
	
	/** Return state. */
	method int state() {
		return state;
	}
	
	/** Set state. */
	method void set_state(int s) {
		let state = s;
		return;
	}
	
	/** Return x. */
	method int x() {
		return x;
	}
	
	/** Return y. */
	method int y() {
		return y;
	}
	
	/** Return width. */
	method int width() {
		return width;
	}
	
	/** Return height. */
	method int height() {
		return height;
	}
	
}

Paddle

/** PADDLE */

class Paddle{

	// screen location of the square's top-left corner
	field int x, y; 
	// width of the paddle
    field int width; 
	// height of the paddle
	field int height;
	
	/** Constructs a new paddle. */
	constructor Paddle new(int Ax, int Ay, int w, int h) {
		let x = Ax;
		let y = Ay;
		let width = w;
		let height = h;
		do draw();
		return this;
	}
	
	/** Draws the paddle on the screen. */
	method void draw() {
		do Screen.setColor(true);
		do Screen.drawRectangle(x, y, x + width, y + height);
		return;
	}
	
   /** Moves the paddle left by 2 pixels. */
   method void moveLeft() {
      if (x > 1) {
         do Screen.setColor(false);
         do Screen.drawRectangle((x + width) - 1, y, x + width, y + height);
         let x = x - 2;
         do Screen.setColor(true);
         do Screen.drawRectangle(x, y, x + 1, y + height);
      }
      return;
   }
   
   /** Moves the paddle right by 2 pixels. */
   method void moveRight() {
      if ((x + width) < 510) {
         do Screen.setColor(false);
         do Screen.drawRectangle(x, y, x + 1, y + height);
         let x = x + 2;
         do Screen.setColor(true);
         do Screen.drawRectangle((x + width) - 1, y, x + width, y + height);
      }
      return;
   }
   
	/** Erases the paddle from the screen. */
	method void erase() {
		do Screen.setColor(false);
		do Screen.drawRectangle(x, y, x + width, y + height);
		return;
	}
   
	/** Return x. */
	method int x() {
		return x;
	}
	
	/** Return y. */
	method int y() {
		return y;
	}
	
	/** Return width. */
	method int width() {
		return width;
	}
	
	/** Return height. */
	method int height() {
		return height;
	}
   
}

Breakout game

/** Breakout game */

class Breakoutgame{
	// brick
	field Brick brick;
	// width of the brick
	field int width;
	// height of the brick
	field int height;
	// number of bricks in each row
	field int row;
	// number of rows of bricks
	field int col;
	// brick's start location of x
	field int x;
	// brick's start location of y
	field int y;
	// separation between neighboring bricks, in pixels
	field int sep;
	// array to store bricks
	field Array Bricks;
	
	// Counter
	field int i, j;

	// paddle
	field Paddle paddle;
	// width of the paddle
	field int paddle_width;
	// height of the paddle
	field int paddle_height;
	// x location of paddle
	field int paddle_x;
	// y location of paddle
	field int paddle_y;
	// paddle's direction
	field int direction;
	
	// ball
	field Ball ball;
	// x location of ball
	field int ball_x;
	// y location of ball
	field int ball_y;
	// r of ball
	field int ball_r;
	
	// whether fail
	field int flag;
	// judge whether hit the brick in a double loop
	field int judge;
	// judge whether hit the brick in a loop
	field int p;
	// count the number of finished bricks
	field int cnt;
	
	/** Constructs a new Breakout Game. */
	constructor Breakoutgame new() {
		// brick
		let row = 5;
		let col = 8;
		let width = 60;
		let height = 12;
		let x = 0;
		let y = 30;
		let sep = 4;
		let Bricks = Array.new(row * col);
		
		// paddle
		let paddle_height = 10;
		let paddle_width = 80;
		let paddle_x = 216;
		let paddle_y = 240;
		let direction = 0;
		
		// ball
		let ball_x = 256;
		let ball_y = 200;
		let ball_r = 2;
		
		// counter
		let i = 0;
		let j = 0;
		let cnt = 0;
		
		// initialization
		let flag = 0;
		let judge = 0;
		let p = 0;
		
		// Set up bricks
		while (i < col){
			let j = 0;
			while (j < row){
				let brick = Brick.new(x + (i * (width + sep)), y + (j * (height + sep)),
									  width, height);
				let Bricks[i * row + j] = brick;
				let j = j + 1;
			}
			let i = i + 1;
		}

		// Set up ball
		let ball = Ball.new(ball_x, ball_y, ball_r);
		
		// Set up paddle
		let paddle = Paddle.new(paddle_x, paddle_y, paddle_width, paddle_height);
		
		// Run the game
		do run();

		return this;
	}
	
	/** Moves the paddle in the current direction. */
	method void movePaddle() {
		//do Output.printInt(1);
		if (direction = 1) { do paddle.moveLeft(); }
		if (direction = 2) { do paddle.moveRight(); }
		do Sys.wait(3);  // delays the next movement
		return;
	}
	
	/** Run the game */
	method void run() {
		var char key;  // the key currently pressed by the user
		var boolean exit;
		let exit = false;

		while (~exit) {
			// waits for a key to be pressed
			while (key = 0) {
				let key = Keyboard.keyPressed();
				do movePaddle();
				// if fail, restart
				if (flag = 1) {
					do paddle.erase();
					do ball.erase();
					let paddle = Paddle.new(paddle_x, paddle_y, paddle_width, paddle_height);
					let ball = Ball.new(ball_x, ball_y, ball_r);
					let flag = 0;
				}
				// refresh the state
				do Ball_state();
				// move the ball
				do Ball_run();
				// if finish, break the game
				if (cnt = (row * col)) {
					let exit = true;
				}
			}
			if (key = 81)  { let exit = true; }     // q key
			if (key = 130) { let direction = 1; }   // left arrow
			if (key = 132) { let direction = 2; }   // right arrow

			// waits for the key to be released
			while (~(key = 0)) {
				let key = Keyboard.keyPressed();
				do movePaddle();
				// if fail, restart
				if (flag = 1) {
					do Output.printInt(1);
					do paddle.erase();
					do ball.erase();
					let paddle = Paddle.new(paddle_x, paddle_y, paddle_width, paddle_height);
					let ball = Ball.new(ball_x, ball_y, ball_r);
					let flag = 0;
				}
				// refresh the state
				do Ball_state();
				// move the ball
				do Ball_run();
				// if finish, break the game
				if (cnt = (row * col)) {
					let exit = true;
				}
			}
		} // while
		
		
		return;
	}
	
	/** Change the state of the ball */
	method void Ball_state() {
	
		// change the state when hit the wall
		if (((ball.x() + ball.r()) = 511)) {
			do ball.set_directionx(1);
		}

		if (((ball.x() - ball.r()) = 1)) {
			do ball.set_directionx(0);
		}
		
		if (((ball.y() + ball.r()) = 255)) {
			do ball.set_directiony(1);
		}

		if (((ball.y() - ball.r()) = 1)) {
			do ball.set_directiony(0);
		}
		
		// change the state when hit the paddle
		if (((ball.y() + ball.r()) = (paddle.y() - 1)) & (ball.x() > paddle.x())
												 & (ball.x() < (paddle.x() + paddle_width))) {
			do ball.set_directiony(1);
		}
		
		// judge whether fail
		if (((ball.y() + ball.r()) > (paddle.y() - 1)) | 
			(((ball.y() + ball.r()) = paddle.y()) & (ball.x() < paddle.x())) |
			(((ball.y() + ball.r()) = paddle.y()) & (ball.x() > (paddle.x() + paddle_width)))) {	
			let flag = 1;
			return;
		}
		
		// change the state when hit the brick
		// Counter
		let i = 0;
		let j = 0;
		let p = 0;
		
		while ((i < col) & (p = 0)){
			let j = 0;
			while ((j < row) & (p = 0)){
				let brick = Bricks[i * row + j];
				let judge = 0;
				if (brick.state() = 1) {
					if (((ball.x() + ball.r()) = brick.x()) &
						(ball.y() > (brick.y() - ball_r)) &
						(ball.y() < (brick.y() + brick.height() + ball_r)) &
						(judge = 0)) {
						do ball.set_directionx(1);
						do brick.set_state(0);
						do brick.erase();
						let judge = 1;
						let p = 1;
						let Bricks[i * row + j] = brick;
						let cnt = cnt + 1;
					}

					if (((ball.x() - ball.r()) = (brick.x() + brick.width())) &
						(ball.y() > (brick.y() - ball_r)) &
						(ball.y() < (brick.y() + brick.height() + ball_r)) &
						(judge = 0)) {
						do ball.set_directionx(0);
						do brick.set_state(0);
						do brick.erase();
						let judge = 1;
						let p = 1;
						let Bricks[i * row + j] = brick;
						let cnt = cnt + 1;
					}
					
					
					if (((ball.y() + ball.r()) = brick.y()) &
						(ball.x() > (brick.x() - ball_r)) & 
						(ball.x() < (brick.x() + brick.width() + ball_r)) &
						(judge = 0)){
						do ball.set_directiony(1);
						do brick.set_state(0);
						do brick.erase();
						let judge = 1;
						let p = 1;
						let Bricks[i * row + j] = brick;
						let cnt = cnt + 1;
					}

					if (((ball.y() - ball.r()) = (brick.y() + brick.height())) &
						(ball.x() > (brick.x() - ball_r)) & 
						(ball.x() < (brick.x() + brick.width() + ball_r)) &
						(judge = 0)) {
						do ball.set_directiony(0);
						do brick.set_state(0);
						do brick.erase();
						let judge = 1;
						let p = 1;
						let Bricks[i * row + j] = brick;
						let cnt = cnt + 1;
					}
				}
				let j = j + 1;
			}
			let i = i + 1;
		}
		return;
	}
	
	
	/** ball_run */
	method void Ball_run() {
		if ((ball.directionx() = 0) & (ball.directiony() = 0)){
			do ball.moveDownRight();
			//do Sys.wait(1);
		}
		if ((ball.directionx() = 0) & (ball.directiony() = 1)){
			do ball.moveUpRight();
			//do Sys.wait(1);
		}
		if ((ball.directionx() = 1) & (ball.directiony() = 0)){
			do ball.moveDownLeft();
			//do Sys.wait(1);
		}
		if ((ball.directionx() = 1) & (ball.directiony() = 1)){
			do ball.moveUpLeft();
			//do Sys.wait(1);
		}	
		return;
	}

}

Main

/** Initializes a new Breakout game and starts running it. */
class Main {
	
    function void main() {
		var BreakoutGame game;
		let game = Breakoutgame.new();
        return;
    }
}