CS61c Proj1 Snake

Mar 14, 2025

Overview

游戏的所有对象都由一个或多个特定字符来表示

##############
#            #
#    dv      #
#     v   #  #
#     v   #  #
#   s >>D #  #
#   v     #  #
# *A<  *  #  #
#            #
##############
main() {
	Read board from file or stdin or create a default board
	update game
	write updated board to file or stdout
}

struct

typedef struct snake_t {
  unsigned int tail_row;
  unsigned int tail_col;
  unsigned int head_row;
  unsigned int head_col;

  bool live;
} snake_t;

typedef struct game_t {
  unsigned int num_rows;
  char **board;

  unsigned int num_snakes;
  snake_t *snakes;
} game_t;

update_game()

board file预设了所有对象的位置:蛇,水果,墙
在加载board之后,一时刻的游戏运行逻辑:
蛇的移动实际上只需要更新头部和尾部的位置,即 update_head(), update_tail()
两个函数根据下一刻的位置是空地,墙还是水果做不同的更新 而这两个函数的实现有许多helper完成,大概来说有:

load_board()

一个问题是在不知道文件中的board有多大的情况下,而且在C中无法通过不读取来获取board的大小,因为FILEobject type不包含也不蕴含这一信息

Object type that identifies a stream and contains the information needed to control it, including a pointer to its buffer, its position indicator and all its state indicators.

要向game->board分配多大的内存呢? 答案是动态分配,与一个动态数组类似,当读取的行数超过分配的大小时,原来的大小翻倍,通过realloc()重新分配内存。game->board[r]的内存分配也是如此,和其他语句结合,封装在另一个函数read_line()中,通过fgets()copy string from stream,整体逻辑是

  1. 在while循环中,在fgets()之后检查读取的字符串最后一个字符是否为\n
  2. 若不是,则说明有更多的字符需要读取到这一行,fgets()被截断,大小翻倍,line重新分配内存

line指针不移动,通过pointer arithmetic来计算从哪里开始读取新的num个字符 heuristically, 有两个变量size: 分配的内存大小, len: 实际大小。由此来计算fgets()的参数str, num 自动附加的\0需要被覆写,所以读取的num有+1,len因为不包含\0,所以作为offset,值刚刚好

char *read_line(FILE *fp) {
    char *line = malloc(256);
    int size = 256;
    int len = 0;
    while ((fgets(line + len, size - len + 1, fp)) != NULL) {
        // check if fgets() is truncated
        len = strlen(line);
        if (line[len-1] != '\n' && !feof(fp)) {
            size *= 2;
            line = realloc(line, (size_t) size);
            continue;
        }
        return line;
    }
    free(line);
    return NULL;
}

snake的初始化是与load_board分离的,实现很简单

  1. 扫描board找到tail位置
  2. 跟随蛇体找到蛇头
  3. 填写snake的各个field

到此为止cs61c/proj1 的所有工作已完成,因为不是从头写起的,骨架都搭好了,难度不大,作为C语言初学者的练习再合适不过了。