PS4 · Memory Scramble

记忆扰乱:并发与网络

Board ADT、HTTP 协议交互、并发等待、map/watch 回调——6.102 的收官之战,将前三课的 ADT 技能与并发编程融合。

基本信息

PS4并发异步HTTP安全

官方讲义:web.mit.edu/6.102/ps4 ↗

作业要求

  1. 设计/测试可变 Board ADT 和 parseFromFile()
  2. 连接字符串命令 API:look() 和 flip()
  3. 改造 Board 支持并发玩家。首次翻牌可能需要无忙等等待
  4. 实现异步 map 操作,保持可观察的成对一致性
  5. 实现 watch/变更通知
官方讲义给出了 board-file 文法、board-state 协议和详细的多玩家规则。在设计 Board API 之前直接阅读这些规则;公开 TypeDoc 有意只暴露 parseFromFile,因为大多数 Board 操作由学生设计。

规格说明

Board 文件格式

第一行 ROWSxCOLUMNS,随后恰好 ROWS*COLUMNS 行卡片标记(行优先)。Board.parseFromFile(filename) 返回 Promise。

Board-state 线协议

响应以 ROWSxCOLUMNS 开始,然后每行一个空格:

翻牌规则(摘要)

第一张牌:

第二张牌:

下一张第一张牌之前:

命令 API

look(board, playerId): Promise<string>
flip(board, playerId, row, column): Promise<string>
map(board, playerId, f): Promise<string>
watch(board, playerId): Promise<string>

并发设计笔记

定义 Board 抽象状态、玩家状态、原子操作、等待条件、唤醒条件和 RI,然后实现并发。

完成清单

源码骨架

board.ts — Board ADT 与文件解析

import { readFile } from 'node:fs/promises';

export class Board {
  // TODO: 指定可变、并发安全的抽象并选择表示
  private constructor() {}

  public static async parseFromFile(filename:string):Promise<Board> {
    // 读取文件使 I/O 管道可用,但验证/解析内容仍是你的任务
    const contents=await readFile(filename,'utf8'); void contents;
    throw new Error('TODO: validate board-file grammar and construct Board');
  }
}

commands.ts — 命令 API

import type { Board } from './board.js';

export async function look(board:Board, playerId:string):Promise<string> { void board; void playerId; throw new Error('TODO: look'); }
export async function flip(board:Board, playerId:string, row:number, column:number):Promise<string> { void board; void playerId; void row; void column; throw new Error('TODO: flip'); }
export async function map(board:Board, playerId:string, f:(card:string)=>Promise<string>):Promise<string> { void board; void playerId; void f; throw new Error('TODO: map'); }
export async function watch(board:Board, playerId:string):Promise<string> { void board; void playerId; throw new Error('TODO: watch'); }

server.ts — 调试服务器

import http from 'node:http';
import type { Board } from './board.js';
import { flip, look, map, watch } from './commands.js';

/** Small independently-written debug server using a simple JSON-ish query protocol. */
export function startServer(board:Board, port:number):http.Server {
  const server=http.createServer(async (req,res)=>{
    try {
      const url=new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
      const player=url.searchParams.get('player') ?? 'player'; let body:string;
      if(url.pathname==='/look') body=await look(board,player);
      else if(url.pathname==='/flip') body=await flip(board,player,Number(url.searchParams.get('row')),Number(url.searchParams.get('col')));
      else if(url.pathname==='/watch') body=await watch(board,player);
      else if(url.pathname==='/map') body=await map(board,player, async card => card);
      else { res.statusCode=404; body='not found'; }
      res.setHeader('content-type','text/plain; charset=utf-8'); res.end(body);
    } catch(e) { res.statusCode=400; res.end(e instanceof Error ? e.message : String(e)); }
  });
  server.listen(port); return server;
}