【build a computer】 project 9

week9

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

project

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

  • 循环队列实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    /* 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;
    }
    }
  • 随机数生成器: 这个可以在维基百科上找到线性方法生成简单的随机数的原理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    /*
    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;
    }
    }
  • 游戏的逻辑主体部分,不过写的过于复杂点了,其实还可以写的更简单一些,或者完成俄罗斯方块游戏之类的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    /* 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;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    /* 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;
    }
    }

【build a computer】 project 9
http://example.com/2026/09/08/公开课学习/82/
Author
Mike Meng
Posted on
September 8, 2026
Licensed under