且听疯吟

【build a computer】 project 9

2022-11-20

week9

目前已经开始了build a computer系列的最后几章了,后面的章节感觉越来越难,build a computerpart II部分基本上全是软件,需要编码,耗费大量的实践和精力来应付这些代码,不过能够最终通过测试的话还是成就满满,特别是只剩下后面三章了,主要是完成一个jack语言编译器和一个最基本的微型操作系统,并提供基本的系统库函数。感觉难度和挑战还是挺高的,非常喜欢这类基础的课程,要是这些课程放在大学本科该多好,有这么好的教程和资源,却一直从来没有机会接触到这么好的教育资源。其实从内心来说,叶不想让自己的孩子再重复一遍国内这种糟糕的教育,希望孩子能够正常成长吧,等他长大以后,作为老父亲的我能够把自己亲身体会到一些先进的理念和知识体系传授给他。

project

本周的project是完全任意发挥,就是利用jack语言自己实现一个小游戏之类的,我用jack语言实现了一个贪吃蛇的小游戏,感觉还是非常有意思的。在jack语言中,重新设计了缓存和随机数生成器,题目本身不难,但是如果想设计出来有意思的还是不太容易。大概编码加调试花了两个半天的时间,现在时常感觉有非常多有意思的课程和知识可以学习,但是因为家庭的原因,根本没有时间能够话费在自己的爱好和兴趣上。今天读书的时候,书中写到了一句话,叫做四个责任,“对自己负责,对家庭负责,对工作负责,对社会负责”,这是依次递进的关系,从小到大的原则。
最终完成的project代码如下:
代码
游戏运行截图如下:
1
本身project难度不大,但是如何发挥出聪明才智,看到好多老外真心有创意,利用jack语言做了许多非常有意思的游戏,记得映像比较深刻的,有人做了个射击游戏,还有人做了个汉诺塔的游戏。
在此项目完成过程,主要就是重新设计了循环队列之类的。

  • 循环队列实现
    /* GluttonousSnake */
    /* created by mike meng @ 2020.10.3
    * email: mml1106@126.com
    */
    class Queue {
    field Array buffer;
    field int capbility;
    field int rear;
    field int front;
    field int length;

    constructor Queue new(int size){
    let capbility = size;
    let front = 0;
    let rear = 0;
    let buffer = Memory.alloc(size);
    let length = 0;
    return this;
    }

    method void dispose(){
    do buffer.dispose();
    do Memory.deAlloc(this);
    return;
    }

    method boolean isEmpty(){
    return (rear = front);
    }

    method int size(){
    return length;
    }

    method boolean isFull(){
    var int check;
    let check = Snake.mod(rear + 1,capbility);
    return (check = front);
    }

    method boolean push_back(int val){
    /*check the Queue is Full*/
    if(isFull()){return false;}
    let length = length + 1;
    let buffer[rear] = val;
    let rear = Snake.mod(rear + 1,capbility);
    return true;
    }

    method boolean push_front(int val){
    /*check the Queue is Full*/
    if(isFull()){ return false;}
    let length = length + 1;
    let front = Snake.mod(front - 1 + capbility,capbility);
    let buffer[front] = val;
    return true;
    }

    method int pop_front(){
    var int res;
    if(isEmpty()){return -1;}
    let res = buffer[front];
    let front = Snake.mod(front + 1,capbility);
    let length = length - 1;
    return res;
    }

    method int pop_back(){
    var int res;
    /*check the Queue is Full*/
    if(isEmpty()){return -1;}
    let length = length - 1;
    let rear = Snake.mod(rear - 1 + capbility,capbility);
    return buffer[rear];
    }

    method void print(){
    var int curr;
    var int data;
    /*debug all elements of the queue*/
    let curr = length;
    while(~(curr = 0)){
    let data = pop_front();
    do Output.printInt(data);
    do Output.printChar(32); // prints a space
    do push_back(data);
    let curr = curr - 1;
    }
    return;
    }
    }
  • 随机数生成器: 这个可以在维基百科上找到线性方法生成简单的随机数的原理
    /*
    Random Number Generator
    Original author: Taylor Wacker
    Modified by: Connor McKay

    This is a pseudo random number generator that uses the
    Linear Congruential Generator (LCG) to generate random
    numbers.
    */
    class Random {
    static int x;

    /*
    Sets a new seed value.
    */
    function void seed(int seed) {
    let x = seed;
    return;
    }

    /*
    Returns a mod b. b must be positive.
    */
    function int mod(int a, int b) {
    if (a < 0) {
    let a = -a;
    }

    while ((a + 1) > b) {
    let a = a - b;
    }

    return a;
    }


    /*
    Returns the next random number. Can be negative or positive.
    */
    function int next() {
    let x = Random.mod(7919 + (17*x),7919);
    return x;
    }

    /*
    Returns a random value between x (inclusive) and y (non-inclusive).
    y must be greater than x.
    */
    function int between(int x, int y) {
    var int diff;
    let diff = y - x;
    return Random.mod(Random.next(), diff) + x;
    }
    }
  • 游戏的逻辑主体部分,不过写的过于复杂点了,其实还可以写的更简单一些,或者完成俄罗斯方块游戏之类的。
    /* GluttonousSnake */
    /* created by mike meng @ 2020.10.3
    * email: mml1106@126.com
    */
    class Snake {
    field int row;//
    field int col;//width
    field int bsize;//square size
    field Array head; /*snake Queuehead*/
    field Queue foods; /*current foods*/
    field int foodCount; // food number count
    field Queue body; // current snake body;
    field int speed; //snake speed
    field Array mask;// direction mask
    field int direction; /*-1: none, 0: up, 1: right, 2 : down, 3 : left*/
    field int state; //current state;
    field int score; //current score;

    /** Constructs a new square with a given location and size. */
    constructor Snake new(int w,int h){
    var int i;
    var int val;

    /*matrix height & width*/
    let row = h;
    let col = w;
    let bsize = 16;

    /*game state: 0: runing, 1: game over*/
    let state = 0;

    /*snake head*/
    let head = Array.new(2);
    let head[0] = 0;
    let head[1] = 0;
    do drawBlock(0,0);

    /*snake foods*/
    let foods = Queue.new(100);
    let i = 20;
    do Random.seed(1);
    while(~(i = 0)){
    let val = Snake.mod(Random.next(),row*col);
    do foods.push_back(val);
    //do Snake.debug(val);
    do drawFood(val);
    let i = i - 1;
    }

    /*snake body*/
    let body = Queue.new(row*col);
    do body.push_back(0);

    /*snake direction*/
    /*-1: none, 0: up, 1: right, 2 : down, 3 : left*/
    let mask = Array.new(5);
    let direction = 1;
    let mask[0] = -1;
    let mask[1] = 0;
    let mask[2] = 1;
    let mask[3] = 0;
    let mask[4] = -1;

    /*snake score*/
    let score = 0;

    return this;
    }

    /** Disposes this square. */
    method void dispose() {
    do head.dispose();
    do foods.dispose();
    do body.dispose();
    do mask.dispose();
    do Memory.deAlloc(this);
    return;
    }

    /*math mod function*/
    function int mod(int m,int n){
    return (m - ((m/n)*n));
    }

    /** game is over**/
    method boolean isOver(){
    return state = 1;
    }

    /** snake move up **/
    method void moveUp() {
    if(direction = 2){return;}
    let direction = 0;
    return;
    }

    /** snake move down **/
    method void moveDown() {
    if(direction = 0){return;}
    let direction = 2;
    return;
    }

    /** snake move left **/
    method void moveLeft() {
    if(direction = 1){return;}
    let direction = 3;
    return;
    }

    /** snake move right **/
    method void moveRight() {
    if(direction = 3){return;}
    let direction = 1;
    return;
    }

    /*draw a block*/
    method void drawBlock(int rx,int cx){
    do Screen.setColor(true);
    do Screen.drawRectangle(cx*16,(rx+2)*16,cx*16 + 15,(rx+2)*16 + 15);
    return;
    }

    /*erase a block*/
    method void eraseBlock(int location){
    var int rx;
    var int cx;

    let rx = location/col;
    let cx = Snake.mod(location,col);
    do Screen.setColor(false);
    do Screen.drawRectangle(cx*16,(rx+2)*16,cx*16 + 15,(rx+2)*16 + 15);
    return;
    }

    /*debug*/
    function void debug(int x){
    do Screen.setColor(true);
    do Output.moveCursor(0,0);
    do Output.printInt(x);
    return;
    }

    /*draw a food*/
    method void drawFood(int location){
    var int rx;
    var int cx;

    let rx = location/col;
    let cx = Snake.mod(location,col);
    do Screen.setColor(true);
    do Screen.drawLine(cx*16,(rx+2)*16,cx*16 + 15,(rx+2)*16);
    do Screen.drawLine(cx*16,(rx+2)*16,cx*16,(rx+2)*16 + 15);
    do Screen.drawLine(cx*16,(rx+2)*16,cx*16 + 15,(rx+2)*16 + 15);
    do Screen.drawLine(cx*16,(rx+2)*16 + 15,cx*16 + 15,(rx+2)*16 + 15);
    do Screen.drawLine(cx*16+15,(rx+2)*16,cx*16 + 15,(rx+2)*16 + 15);
    do Screen.drawLine(cx*16+15,(rx+2)*16,cx*16,(rx+2)*16 + 15);
    return;
    }

    /*draw snake head*/
    method void drawHead(int rx,int cx){
    do Screen.setColor(true);
    do Screen.drawLine(cx*16,(rx+2)*16,cx*16 + 15,(rx+2)*16 + 15);
    do Screen.drawLine(cx*16+15,(rx+2)*16,cx*16,(rx+2)*16 + 15);
    return;
    }

    /*snake move one step*/
    method boolean move() {
    var int x;
    var int y;
    var int i;
    var int val;
    var int rx;
    var int cx;
    var boolean eat;

    if(isOver()){ return false;}
    if(direction < 0){ return true;}

    /*get the current move location*/
    let x = head[0] + mask[direction];
    let y = head[1] + mask[direction + 1];
    let i = 0;

    /*check the snake will touch the edge*/
    if((x < 0)|(y < 0)|(x = row)|(y = col)){
    let state = 1;
    return false;
    }

    /*check the snake will touch the body*/
    let i = body.size();
    while(~(i = 0)){
    let val = body.pop_front();
    let i = i - 1;
    let rx = val/col;
    let cx = Snake.mod(val,col);
    if((rx = x) & (cx = y)){
    let state = 1;
    return false;
    }
    do body.push_back(val);
    }
    if(isOver()){ return false;}

    /*check the snake touch the foods*/
    let i = foods.size();
    while(~(i = 0)){
    let i = i - 1;
    let val = foods.pop_front();
    let rx = val/col;
    let cx = Snake.mod(val,col);
    if((rx = x) & (cx = y)){
    let eat = true;
    let score = score + 1;
    }else{
    do foods.push_back(val);
    }
    }

    /*move snake one step*/
    let head[0] = x;
    let head[1] = y;
    do drawBlock(x,y);
    do body.push_front(x*col + y);
    if(~eat){
    do eraseBlock(body.pop_back());
    }else{
    /*we can produce a random food for the snake*/
    //do prodFood();
    }

    return true;
    }

    method int prodFood(){
    var boolean valid;
    var int location;
    var int val;
    var int i;

    let valid = false;
    while(~valid){
    let location = Snake.mod(Random.next(),row*col);
    let i = body.size();
    let valid = true;
    while(~(i=0)){
    let val = body.pop_front();
    do body.push_back(val);
    if(val = location){
    let valid = false;
    }
    }
    }
    do drawFood(location);
    do foods.push_back(location);
    return location;
    }

    /*get the game score*/
    method int getScore(){
    return score;
    }
    }
    /* GluttonousSnake */
    /* created by mike meng @ 2020.10.3
    * email: mml1106@126.com
    */
    class SnakeGame {
    field Snake snake; // the square of this game
    field int width; // the current score of the snake game.
    field int height; // the current score of the snake game.
    field int speed; // the current move speed of the snake game.

    /** Constructs a new Square Game. */
    constructor SnakeGame new(int w,int h) {
    let snake = Snake.new(w,h);
    let speed = 1; // initial state is no movement
    return this;
    }

    /** Disposes this game. */
    method void dispose() {
    do snake.dispose();
    do Memory.deAlloc(this);
    return;
    }

    /**speed up game**/
    method void speedUp(){
    let speed = speed + 1;
    return;
    }

    /**speed down game**/
    method void speedDown(){
    if(speed = 1) {return;}
    let speed = speed - 1;
    return;
    }

    /** Moves the square in the current direction. */
    method void moveSnake() {
    do snake.move();
    do Sys.wait(5); // delays the next movement
    return;
    }

    method void sleep(int seconds){
    do Sys.wait(200/speed);
    return;
    }

    method void drawBackgroud(){
    do Screen.setColor(true);
    do Screen.drawLine(0,0,0,255);
    do Screen.drawLine(0,0,511,0);
    do Screen.drawLine(511,0,511,255);
    do Screen.drawLine(0,255,511,0255);
    do Screen.drawLine(0,30,511,30);
    return;
    }

    method void drawText(){
    do Screen.setColor(true);
    do Output.moveCursor(0,20);
    do Output.printString("Your score is :");
    do Output.printInt(snake.getScore());
    if(snake.isOver()){
    do Output.moveCursor(1,24);
    do Output.printString("Game Over!");
    }
    return;
    }

    /** Runs the game: handles the user's inputs and moves the square accordingly */
    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
    do sleep(1);
    do drawBackgroud();
    do moveSnake();
    do drawText();

    let key = Keyboard.keyPressed();
    if (key = 81) { let exit = true; } // q key
    if (key = 43) { do speedUp();} // speed up
    if (key = 45) { do speedDown();} // speed down
    if (key = 131) { do snake.moveUp();} // up arrow
    if (key = 133) { do snake.moveDown(); } // down arrow
    if (key = 130) { do snake.moveLeft(); } // left arrow
    if (key = 132) { do snake.moveRight(); } // right arrow
    } // while
    return;
    }
    }

扫描二维码,分享此文章