1 实验目标概述
本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象
编程(OOP)技术实现 ADT。具体来说:
针对给定的应用问题,从问题描述中识别所需的 ADT;
设计 ADT 规约(pre-condition、post-condition)并评估规约的质量;
根据 ADT 的规约设计测试用例;
ADT 的泛型化;
根据规约设计 ADT 的多种不同的实现;针对每种实现,设计其表示
(representation)、表示不变性(rep invariant)、抽象过程(abstraction
function)
使用 OOP 实现 ADT,并判定表示不变性是否违反、各实现是否存在表
示泄露(rep exposure);
测试 ADT 的实现并评估测试的覆盖度;
使用 ADT 及其实现,为应用问题开发程序;
在测试代码中,能够写出 testing strategy
2 实验环境配置
实验环境设置请参见 Lab-0 实验指南。
除此之外,本次实验需要你在 Eclipse IDE 中安装配置 EclEmma(一个用于
统计 JUnit 测试用例的代码覆盖度的 plugin)。请访问 http://www.eclemma.org,了解 EclEmma 并学习其安装、配置和使用。
本次实验在 GitHub Classroom 中的 URL 地址为:
https://classroom.github.com/a/z9utaaos
请访问该 URL,按照提示建立自己的 Lab2 仓库并关联至自己的学号。
本地开发时,本次实验只需建立一个项目,统一向 GitHub 仓库提交。实验包 含的 3 个任务分别在不同的目录内开发,具体目录组织方式参见各任务最后一部 分的说明。请务必遵循目录结构,以便于教师/TA 进行测试。
3 实验过程
3.1 Poetic Walks
问题1-3:我们将实现的类型是Graph,带有标记顶点的可变加权有 向图的抽象数据类型。
可变图:可以将顶点和边添加到图中或从图中删除;
有向边:边从源顶点到目标顶点;
加权边:边与正整数权重关联;
标记顶点:顶点通过某种不可变类型的标签来区分,例如,它们可能具有String名称或IntegerID。
问题4:使用我们的图数据类型,我们将实现GraphPoet,一个用于使用单词相似度图生成诗歌的类。
3.1.1 Get the code and prepare Git repository
无法连接 MIT 的 Athena 服务器,请从以下地 址获取初始代码:
https://github.com/rainywang/Spring2020_HITCS_SC_Lab2/tree/master/P1
3.1.2 Problem 1: Test Graph
将Graph里的empty()方法修改为:
并运行GraphStaticTest进行测试:
3.1.3 Problem 2: Implement Graph
3.1.3.1 Implement ConcreteEdgesGraph
1)实现类Edge
Representation:
AF、RI 和Safety from rep exposure:
Constructor:
CheckRep:
Methods:
toString():
2)实现类ConcreteEdgesGraph
Representation:
AF、RI 和Safety from rep exposure:
Constructor:
CheckRep:
Methods:
1.public boolean add(String vertex):将顶点vertex加入到vertices中
2. public int set(String source, String target, int weight):
如果weight为负数,则直接返回-1;
否则,遍历edges;
如果已经存在从source到target的一条边,记下这条边的权值,创建一条新的边e(参数为source,target,weight),调用方法remove删掉edges中的旧边,将新边加入edges,并返回旧边权;
如果不存在,新建一条边(参数为source,target,weight),将其加入edges,并返回0。
3.public boolean remove(String vertex)
检查输入满足vertex存在vertices中,当存在时,删除该节点,同时删除所有与之相连的边
4.public Set vertices()
复制一份vertices到新Set中,返回这个Set
5.public Map<String, Integer> sources(String target)
遍历edges,调用方法getTargetVertex,若找到与target相同的,则将该边加入map中
6.public Map<String, Integer> targets(String source)
遍历edges,调用方法getSourceVertex,若找到与source相同的,则将该边加入map中
7.public String toString()
将所有边的toString连接在一起
3.1.3.2 Implement ConcreteVerticesGraph
1)实现类Vertex:
Representation:
AF、RI 和Safety from rep exposure:
Constructor:
checkRep:
Methods:
toString:
2)实现类ConcreteVerticesGraph
Representation:
AF、RI 和Safety from rep exposure:
Constructor:
Methods:
toString:
3.1.4 Problem 3: Implement generic Graph
3.1.4.1 Make the implementations generic
将类ConcreteEdgesGraph接口改为:
将类ConcreteVerticesGraph接口改为:
并且将类中的所有方法中的String改为L
3.1.4.2 Implement Graph.empty()
将 Graph.empty()改为:
3.1.5 Problem 4: Poetic walks
3.1.5.1 Test GraphPoet
3.1.5.2 Implement GraphPoet
3.1.5.3 Graph poetry slam
main:
结果:
3.1.6 Before you’re done
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
在这里给出你的项目的目录结构树状示意图。
3.2 Re-implement the Social Network in Lab1
在本次实验中,请基于你在 3.1 节 Poetic Walks 中定义的 Graph及其两种 实现,重新实现 Lab1 中 3.3 节的 FriendshipGraph 类。
注 1:可以忽略你在 Lab1 中实现的代码,无需其基础上实现本次作业;
注 2:在本节 FriendshipGraph中,图中的节点仍需为 Person类型。故新的FriendshipGraph 类要利用3.1节已经实现的ConcreteEdgesGraph 或 ConcreteVerticesGraph,L 替换为 Person。根据 Lab1 的要求,FriendshipGraph 中应提供 addVertex()、addEdge()和 getDistance()三个方法:针对addVertex() 和 addEdge() ,你需要尽可能复用ConcreteEdgesGraph或 ConcreteVerticesGraph中已经实现的 add()和 set()方法,而不是从 0 开始写代码实现或者把你的 Lab1 相关代码直接复制过来;针对 getDistance()方法,请基于你所选定的ConcreteEdgesGraph或 ConcreteVerticesGraph的 rep 来实现,而不能修改其 rep。
注 3:不变动 Lab1 的 3.3 节给出的客户端代码(例如 main()中的代码),即 同样的客户端代码仍可运行。重新执行你在 Lab1 里所写的 JUnit 测试用例,测试你在本实验里新实现的 FriendshipGraph 类仍然表现正常。
3.2.1 FriendshipGraph类
接口改为:
3.2.2 Person类
3.2.3 客户端main()
3.2.4 测试用例
3.2.5 提交至Git仓库
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
在这里给出你的项目的目录结构树状示意图。
3.3 Playing Chess
3.3.1 ADT设计/实现方案
1)实现类Position:
public void setPosition(int x,int y):修改横纵坐标
public int getPositionX()与public int getPositionY():获取横纵坐标
public boolean equals(Position that) 判断两个Position相等
2)实现类Piece:
public void setState(int state):设置点的状态
public void setPosition(Position p):设置点的坐标
public String toString():生成包含点信息的字符串
public String getPlayer() :Player的getter
public String getType() :Type的getter
public int getState():State的getter
public Position getPosition() :Position的getter
3)实现类Board:
public int getSize():size的getter
public int getType():type的getter
public Piece[][] getPieces():pieces的getter
public boolean[][] getIsPlaceds():isplaced的getter
public Piece getPiece(int x, int y):得到棋盘上位置为(x,y)的棋子
public boolean getIsPlaced(int x, int y):得到棋盘上位置(x,y)是否有棋子
public void setIsPlaced(int x, int y):将棋盘上位置(x,y)设置有棋子
public void setIsNotPlaced(int x, int y);将棋盘上位置(x,y)设置无棋子
public void setPiece(int x, int y, Piece p):将棋子p放到棋盘上位置(x,y)
public void removePiece(int x, int y):将棋盘上位置(x,y)的棋子移走
public String toString():将棋盘信息转换为字符串
4)实现类Player:
public String getName():name的getter
public Set getPieces():pieces的getter
public List getHistory():history的getter
public int getNumberOfPieces():得到player棋盘上的棋子数
public void addHistory(String step):将操作step加入到player的历史操作中
public boolean addPiece(Piece p):将点p加入到player所有的棋子中
public boolean removePiece(Piece p):将点p从player所有的棋子中移除
5)编写接口Action:
public Player getPlayer1():player1的getter
public Player getPlayer2():player2的getter
public Board getBoard():board的getter
public Player createPlayer1(String playerName):根据playerName生成player1
public Player createPlayer2(String PlayerName):根据playerName生成player2
public Board CreateBoard():生成游戏棋盘
public Piece CreatePiece(String player, String type, int x, int y):生成游戏棋子
public boolean movePiece(Player player, int sourceX, int sourceY, int targetX, int targetY):player将(sourceX,source Y)的棋子移动到(targetX,targetY)的位置
public boolean eatPiece(Player player, int sourceX, int sourceY, int targetX, int targetY):player将(sourceX,source Y)的棋子移动到(targetX,targetY)的位置,并且吃掉(targetX,targetY)的位置上对方的棋子
public boolean removePiece(Player player, int x, int y):将(x,y)位置上的棋子从棋盘上移走
public boolean placePiece(Player player, int x, int y):将棋子落到棋盘的(x,y)位置上
public void initialize(String playerName1, String playerName2):用于初始化游戏,生成游戏玩家、棋盘和棋子
6)实现类ChessAction:
@Override
public Board CreateBoard() {
Board chessBoard = new Board(size, 1);
Piece king1 = CreatePiece(this.player1.getName(), "King", 0, 3);
chessBoard.setPiece(0, 3, king1);
Piece queen1 = CreatePiece(this.player1.getName(), "Queen", 0, 4);
chessBoard.setPiece(0, 4, queen1);
Piece rook11 = CreatePiece(this.player1.getName(), "Rook", 0, 0);
chessBoard.setPiece(0, 0, rook11);
Piece rook12 = CreatePiece(this.player1.getName(), "Rook", 0, 7);
chessBoard.setPiece(0, 7, rook12);
Piece bishop11 = CreatePiece(this.player1.getName(), "Bishop", 0, 2);
chessBoard.setPiece(0, 2, bishop11);
Piece bishop12 = CreatePiece(this.player1.getName(), "Bishop", 0, 5);
chessBoard.setPiece(0, 5, bishop12);
Piece knight11 = CreatePiece(this.player1.getName(), "Knight", 0, 1);
chessBoard.setPiece(0, 1, knight11);
Piece knight12 = CreatePiece(this.player1.getName(), "Knight", 0, 6);
chessBoard.setPiece(0, 6, knight12);
for (int i = 0; i < size; i++) {
Piece pawn1 = CreatePiece(this.player1.getName(), "Pawn", 1, i);
chessBoard.setPiece(1, i, pawn1);
}
Piece king2 = CreatePiece(this.player2.getName(), "King", 7, 3);
chessBoard.setPiece(7, 3, king2);
Piece queen2 = CreatePiece(this.player2.getName(), "Queen", 7, 4);
chessBoard.setPiece(7, 4, queen2);
Piece rook21 = CreatePiece(this.player2.getName(), "Rook", 7, 0);
chessBoard.setPiece(7, 0, rook21);
Piece rook22 = CreatePiece(this.player2.getName(), "Rook", 7, 7);
chessBoard.setPiece(7, 7, rook22);
Piece bishop21 = CreatePiece(this.player2.getName(), "Bishop", 7, 2);
chessBoard.setPiece(7, 2, bishop21);
Piece bishop22 = CreatePiece(this.player2.getName(), "Bishop", 7, 5);
chessBoard.setPiece(7, 5, bishop22);
Piece knight21 = CreatePiece(this.player2.getName(), "Knight", 7, 1);
chessBoard.setPiece(7, 1, knight21);
Piece knight22 = CreatePiece(this.player2.getName(), "Knight", 7, 6);
chessBoard.setPiece(7, 6, knight22);
for (int i = 0; i < size; i++) {
Piece pawn2 = CreatePiece(this.player2.getName(), "Pawn", 6, i);
chessBoard.setPiece(6, i, pawn2);
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < size; j++) {
chessBoard.setIsPlaced(i, j);
Piece p = chessBoard.getPiece(i, j);
distributeChesses(this.player1, p);
}
}
for (int i = 6; i < size; i++) {
for (int j = 0; j < size; j++) {
chessBoard.setIsPlaced(i, j);
Piece p = chessBoard.getPiece(i, j);
distributeChesses(this.player2, p);
}
}
return chessBoard;
}
public boolean movePiece(Player player, int sourceX, int sourceY, int targetX, int targetY):
@Override
public Board CreateBoard() {
Board chessBoard = new Board(size, 1);
Piece king1 = CreatePiece(this.player1.getName(), "King", 0, 3);
chessBoard.setPiece(0, 3, king1);
Piece queen1 = CreatePiece(this.player1.getName(), "Queen", 0, 4);
chessBoard.setPiece(0, 4, queen1);
Piece rook11 = CreatePiece(this.player1.getName(), "Rook", 0, 0);
chessBoard.setPiece(0, 0, rook11);
Piece rook12 = CreatePiece(this.player1.getName(), "Rook", 0, 7);
chessBoard.setPiece(0, 7, rook12);
Piece bishop11 = CreatePiece(this.player1.getName(), "Bishop", 0, 2);
chessBoard.setPiece(0, 2, bishop11);
Piece bishop12 = CreatePiece(this.player1.getName(), "Bishop", 0, 5);
chessBoard.setPiece(0, 5, bishop12);
Piece knight11 = CreatePiece(this.player1.getName(), "Knight", 0, 1);
chessBoard.setPiece(0, 1, knight11);
Piece knight12 = CreatePiece(this.player1.getName(), "Knight", 0, 6);
chessBoard.setPiece(0, 6, knight12);
for (int i = 0; i < size; i++) {
Piece pawn1 = CreatePiece(this.player1.getName(), "Pawn", 1, i);
chessBoard.setPiece(1, i, pawn1);
}
Piece king2 = CreatePiece(this.player2.getName(), "King", 7, 3);
chessBoard.setPiece(7, 3, king2);
Piece queen2 = CreatePiece(this.player2.getName(), "Queen", 7, 4);
chessBoard.setPiece(7, 4, queen2);
Piece rook21 = CreatePiece(this.player2.getName(), "Rook", 7, 0);
chessBoard.setPiece(7, 0, rook21);
Piece rook22 = CreatePiece(this.player2.getName(), "Rook", 7, 7);
chessBoard.setPiece(7, 7, rook22);
Piece bishop21 = CreatePiece(this.player2.getName(), "Bishop", 7, 2);
chessBoard.setPiece(7, 2, bishop21);
Piece bishop22 = CreatePiece(this.player2.getName(), "Bishop", 7, 5);
chessBoard.setPiece(7, 5, bishop22);
Piece knight21 = CreatePiece(this.player2.getName(), "Knight", 7, 1);
chessBoard.setPiece(7, 1, knight21);
Piece knight22 = CreatePiece(this.player2.getName(), "Knight", 7, 6);
chessBoard.setPiece(7, 6, knight22);
for (int i = 0; i < size; i++) {
Piece pawn2 = CreatePiece(this.player2.getName(), "Pawn", 6, i);
chessBoard.setPiece(6, i, pawn2);
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < size; j++) {
chessBoard.setIsPlaced(i, j);
Piece p = chessBoard.getPiece(i, j);
distributeChesses(this.player1, p);
}
}
for (int i = 6; i < size; i++) {
for (int j = 0; j < size; j++) {
chessBoard.setIsPlaced(i, j);
Piece p = chessBoard.getPiece(i, j);
distributeChesses(this.player2, p);
}
}
return chessBoard;
}
public boolean eatPiece(Player player, int sourceX, int sourceY, int targetX, int targetY):
public boolean removePiece(Player player, int x, int y)与
public boolean placePiece(Player player, int x, int y)是象棋中非法操作直接返回false
7)实现类GoAction:
public boolean placePiece(Player player, int x, int y):
public boolean removePiece(Player player, int x, int y):
public boolean isPiecesNumLegal(Player player) :用于判断玩家player的棋子是否用完
public boolean movePiece(Player player, int sourceX, int sourceY, int targetX, int targetY)
与
public boolean eatPiece(Player player, int sourceX, int sourceY, int targetX, int targetY)
是围棋中非法和操作直接返回false
8)实现类Game
一系列的getter分别得到size、player1的name、player2的name、board、player1、player2:
public boolean removePiece(Player player, int x, int y)与
public boolean placePiece(Player player, int x, int y)
public boolean movePiece(Player player, int sourceX, int sourceY, int targetX, int targetY)
与
public boolean eatPiece(Player player, int sourceX, int sourceY, int targetX, int targetY):
并且将吃棋视为移动棋子的一种特例,只需要将目标位置上的棋子移走,就相当于走棋的操作
public boolean checkBoardPlaced(int x, int y):检查(x,y)位置上是否有棋子
public int getNuberOfPiece(String playerName):得到名字为playerName玩家的棋子数
public String getHistorySteps(String playerName):得到名字为playerName玩家的历史操作
public String getPieces(String playerName) :得到名字为playerName玩家的现在的棋子信息
3.3.2 主程序MyChessAndGoGame设计/实现方案
主函数:
public String getPlayerInput(String prompt):用于得到用户的屏幕输入,用于人机交互
public Game init():用于初始化游戏资源
public String inputStep(String playerName):用于读入玩家输入的操作步骤
public void startGoGame(MyChessAndGoGame myGame, Game game, Board board):用于GoGame的进行
public void startGoGame(MyChessAndGoGame myGame, Game game, Board board) {
System.out.println("Input rules: ");
System.out.println("\nIf you want to place the piece in position (x,y)");
System.out.println("Please input: Place x,y");
System.out.println("\nIf you want to remove the piece in position (x,y)");
System.out.println("Please input: Remove x,y");
System.out.println("\nIf you want to skip the current round");
System.out.println("Please input: Skip");
System.out.println("\nIf you want to give up");
System.out.println("Please input: End");
System.out.println("\nStart!");
Player player1 = game.getPlayer1();
Player player2 = game.getPlayer2();
do {
boolean isEnd = false;
do {
String step1 = inputStep(player1.getName()).toLowerCase();
if (step1.equals("end")) {
isEnd = true;
System.out.println("\n" + player2.getName() + " Win!");
break;
}
if (step1.equals("skip"))
break;
String[] s11 = step1.split("\\s");
String[] s12 = s11[1].split(",");
int x1 = Integer.parseInt(s12[0]);
int y1 = Integer.parseInt(s12[1]);
if (s11[0].equals("place")) {
if (game.placePiece(player1.getName(), x1, y1))
break;
System.out.println("Invalid operation! Please re-enter!");
} else {
if (game.removePiece(player1.getName(), x1, y1))
break;
System.out.println("Invalid operation! Please re-enter!");
}
} while (true);
System.out.println("\n" + board.toString());
if (player2.getNumberOfPieces() == 0 && player2.getHistory().size() != 0) {
System.out.println("\n" + player1.getName() + " Win!");
isEnd = true;
}
if (isEnd)
break;
do {
String step2 = inputStep(player2.getName()).toLowerCase();
if (step2.equals("end")) {
isEnd = true;
System.out.println("\n" + player2.getName() + " Win!");
break;
}
if (step2.equals("skip"))
break;
String[] s21 = step2.split("\\s");
String[] s22 = s21[1].split(",");
int x2 = Integer.parseInt(s22[0]);
int y2 = Integer.parseInt(s22[1]);
if (s21[0].equals("place")) {
if (game.placePiece(player2.getName(), x2, y2))
break;
System.out.println("Invalid operation! Please re-enter!");
} else {
if (game.removePiece(player2.getName(), x2, y2))
break;
System.out.println("Invalid operation! Please re-enter!");
}
} while (true);
System.out.println("\n" + board.toString());
if (player1.getNumberOfPieces() == 0 && player1.getHistory().size() != 0) {
System.out.println("\n" + player1.getName() + " Win!");
isEnd = true;
}
if (isEnd)
break;
} while (true);
System.out.println(game.getHistorySteps(player1.getName()));
System.out.println(game.getHistorySteps(player2.getName()));
}
public void startChessGame(MyChessAndGoGame myGame, Game game, Board board) :用于ChessGame的进行
public void startChessGame(MyChessAndGoGame myGame, Game game, Board board) {
System.out.println("\nInput rules: ");
System.out.println("If you want to move the piece in position (x1,y1) to position(x2,y2)");
System.out.println("Please input: Move x1,y1 to x2,y2");
System.out.println("\nIf you want to give up");
System.out.println("Please input: End");
System.out.println("\nIf you want to skip the current round");
System.out.println("Please input: Skip");
System.out.println("\nStart!");
Player player1 = game.getPlayer1();
Player player2 = game.getPlayer2();
do {
boolean isEnd = false;
do {
String step1 = inputStep(player1.getName()).toLowerCase();
if (step1.equals("end")) {
isEnd = true;
System.out.println("\n" + player2.getName() + " Win!");
break;
}
if (step1.equals("skip"))
break;
String[] s11 = step1.split("\\s");
String[] s12 = s11[1].split(",");
String[] s13 = s11[3].split(",");
int sourceX1 = Integer.parseInt(s12[0]);
int sourceY1 = Integer.parseInt(s12[1]);
int targetX1 = Integer.parseInt(s13[0]);
int targetY1 = Integer.parseInt(s13[1]);
if (game.movePiece(player1.getName(), sourceX1, sourceY1, targetX1, targetY1))
break;
System.out.println("Invalid operation! Please re-enter!");
} while (true);
System.out.println("\n" + board.toString());
if (player2.getNumberOfPieces() == 0) {
System.out.println("\n" + player1.getName() + " Win!");
isEnd = true;
}
if (isEnd)
break;
do {
String step2 = inputStep(player2.getName()).toLowerCase();
if (step2.equals("end")) {
System.out.println("\n" + player1.getName() + " Win!");
isEnd = true;
break;
}
if (step2.equals("skip"))
break;
String[] s21 = step2.split("\\s");
String[] s22 = s21[1].split(",");
String[] s23 = s21[3].split(",");
int sourceX2 = Integer.parseInt(s22[0]);
int sourceY2 = Integer.parseInt(s22[1]);
int targetX2 = Integer.parseInt(s23[0]);
int targetY2 = Integer.parseInt(s23[1]);
if (game.movePiece(player2.getName(), sourceX2, sourceY2, targetX2, targetY2))
break;
System.out.println("Invalid operation! Please re-enter!");
} while (true);
System.out.println("\n" + board.toString());
if (player1.getNumberOfPieces() == 0) {
System.out.println("\n" + player2.getName() + " Win!");
isEnd = true;
}
if (isEnd)
break;
} while (true);
System.out.println(game.getHistorySteps(player1.getName()));
System.out.println(game.getHistorySteps(player2.getName()));
}
3.3.3 ADT和主程序的测试方案
主要测试游戏中的4个方法是否正确:
public boolean movePiece(Player player, int sourceX, int sourceY, int targetX, int targetY):
public boolean eatPiece(Player player, int sourceX, int sourceY, int targetX, int targetY):
public boolean removePiece(Player player, int x, int y)
public boolean placePiece(Player player, int x, int y);
然后再对较为底层的类进行测试:
类Piece:
类Player:
类Position: