本项目于3.17日实验课验收,请放心参考
参考时文中有给出一些建议,请查看
基本更新完成
- Software Construction 2020 Spring
- Lab-2 Abstract Data Type (ADT) and Object-Oriented Programming (OOP)
- CSDN博客
依据https://www.eclemma.org/installation.html内容,从更新站点进行安装。
略
该任务主要是实验一个图的模块,并基于此使用。
git clone https://github.com/rainywang/Spring2020_HITCS_SC_Lab2.git
该部分要求重写Graph里的方法,分别以点为基础的图和以边为基础的图。
节选部分较难实现的方法代码
Edge实现
@Override
public int set(L source, L target, int weight) {
if (weight < 0)
throw new RuntimeException("Negative weight");
if (!vertices.contains(source) || !vertices.contains(target)) {
if (!vertices.contains(source))
this.add(source);
if (!vertices.contains(target))
this.add(target);
}
if (source.equals(target)) // source is the same with target, REFUSE to set the Edge.
return 0;
// Find the same edge
Iterator<Edge<L>> it = edges.iterator();
while (it.hasNext()) {
Edge<L> edge = it.next();
if (edge.sameEdge(source, target)) {
int lastEdgeWeight = edge.weight();
it.remove();
if (weight > 0) {
Edge<L> newEdge = new Edge<L>(source, target, weight);
edges.add(newEdge);
}
checkRep();
return lastEdgeWeight;
}
}
// weight=0 means delete an edge, so it can't be before FINDING
if (weight == 0)
return 0;
// new positive edge
Edge<L> newEdge = new Edge<L>(source, target, weight);
edges.add(newEdge);
checkRep();
return 0;
}
@Override
public boolean remove(L vertex) {
if (!vertices.contains(vertex))
return false;
edges.removeIf(edge -> edge.source().equals(vertex) || edge.target().equals(vertex));
vertices.remove(vertex);
checkRep();
return true;
}
Vertex实现
public int setInEdge(L source, int weight) {
if (weight <= 0)
return 0;
Iterator<L> it =inEdges.keySet().iterator();
while (it.hasNext()) {
L key = it.next();
if (key.equals(source)) {
int lastEdgeWeight = inEdges.get(key);
it.remove();
inEdges.put(source, weight);
return lastEdgeWeight;
}
}
inEdges.put(source, weight);
checkRep();
return 0;
}
ConcreteVerticesGraph实现
@Override
public int set(L source, L target, int weight) {
if (weight < 0)
throw new RuntimeException("Negative weight");
if (source.equals(target))
return 0;
Vertex<L> from = null, to = null;
for (Vertex<L> vertex : vertices) {
if (vertex.ThisVertex().equals(source))
from = vertex;
if (vertex.ThisVertex().equals(target))
to = vertex;
}
if (from == null || to == null)
throw new NullPointerException("Inexistent vertex");
int lastEdgeWeight;
if (weight > 0) {
lastEdgeWeight = from.setOutEdge(target, weight);
lastEdgeWeight = to.setInEdge(source, weight);
} else {
lastEdgeWeight = from.removeOutEdge(target);
lastEdgeWeight = to.removeInEdge(source);
}
checkRep();
return lastEdgeWeight;
}
@Override
public boolean remove(L vertex) {
for (Vertex<L> THIS : vertices) {
if (THIS.ThisVertex().equals(vertex)) {
for (Vertex<L> v : vertices) {
if (THIS.sources().containsKey(v)) {
// THIS.removeInEdge(v);
v.removeOutEdge(THIS.ThisVertex());
}
if (THIS.targets().containsKey(v)) {
// THIS.removeOutEdge(v);
v.removeInEdge(THIS.ThisVertex());
}
}
vertices.remove(THIS);
checkRep();
return true;
}
}
checkRep();
return false;
}
public static Graph<String> empty() {
return new ConcreteEdgesGraph();
}
前文指南
问题简述:
1. 表示不变量和检查不变量
2. 构造函数
int lastEdgeWeight = graph.set(words[i - 1].toLowerCase(), words[i].toLowerCase(), 1);
if (lastEdgeWeight != 0) graph.set(words[i - 1].toLowerCase(), words[i].toLowerCase(), lastEdgeWeight + 1);
3. Poem(String input)
当相邻两个单词任意一个不在之前创建的图里,则将后者单词加入即可(再加个空格)当存在时,由于Bridge长度只能为2,所以:分别求两个单词的sources和targets,将该Map转换为Set求交集;若交集为空,则无桥,若交集不空,则在交集中找最短的桥(可以在Map的value中查询weight)。
求交集
targets = graph.targets(words[i - 1].toLowerCase());
sources = graph.sources(words[i].toLowerCase());
intersection = sources.keySet();
intersection.retainAll(targets.keySet());
int maxBridge = Integer.MIN_VALUE;
String bridge = "";
for (String key : intersection) {
if (sources.get(key) + targets.get(key) > maxBridge) {
maxBridge = sources.get(key) + targets.get(key);
bridge = key;
}
}
样例“This is a test of the Mugar Omni Theater sound system.”
进行测试,测试成功。
修改样例为“This is a the Mugar system Omni Theater sound system test of the.”
,测试成功。该样例用于测试极端情况。
请按照 http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done 的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
在这里给出你的项目的目录结构树状示意图。
/**
* get Distance of two of them
*
* @param sta path starting person
* @param end path ending person
* @return distance between 2 persons or -1 when unlinked
*/
public int getDistance(Person sta, Person end) {
if (sta.equals(end))
return 0;
Map<Person, Integer> dis = new HashMap<>();
Map<Person, Boolean> vis = new HashMap<>();
Queue<Person> qu = new LinkedList<Person>();
Set<Person> persons = graph.vertices();
for (Person person : persons) {
dis.put(person, 0);
vis.put(person, false);
}
vis.remove(sta);
vis.put(sta, true);
for (qu.offer(sta); !qu.isEmpty();) {
Person person = qu.poll();
for (Map.Entry<Person, Integer> edge : graph.targets(person).entrySet()) {
Person target = edge.getKey();
if (!vis.get(target)) {
qu.offer(target);
vis.remove(target);
vis.put(target, true);
dis.remove(target);
dis.put(target, dis.get(person) + 1);
if (target.equals(end))
return dis.get(target);
}
}
}
return -1;
}
public class FriendshipGraphTest {
/**
* Basic Network Test
*/
@Test
public void Test1() {
final FriendshipGraph graph = new FriendshipGraph();
final Person rachel = new Person("Rachel");
final Person ross = new Person("Ross");
final Person ben = new Person("Ben");
final Person kramer = new Person("Kramer");
assertEquals(true, graph.addVertex(rachel));
assertEquals(true, graph.addVertex(ross));
assertEquals(true, graph.addVertex(ben));
assertEquals(true, graph.addVertex(kramer));
assertEquals(0, graph.addEdge(rachel, ross));
assertEquals(1, graph.addEdge(ross, rachel));
assertEquals(0, graph.addEdge(ross, ben));
assertEquals(1, graph.addEdge(ben, ross));
assertEquals(1, graph.getDistance(rachel, ross));
assertEquals(2, graph.getDistance(rachel, ben));
assertEquals(0, graph.getDistance(rachel, rachel));
assertEquals(-1, graph.getDistance(rachel, kramer));
}
/**
* Further Test
*/
@Test
public void Test2() {
final FriendshipGraph graph = new FriendshipGraph();
final Person a = new Person("A");
final Person b = new Person("B");
final Person c = new Person("C");
final Person d = new Person("D");
final Person e = new Person("E");
final Person f = new Person("F");
final Person g = new Person("G");
final Person h = new Person("H");
final Person i = new Person("I");
final Person j = new Person("J");
assertEquals(true, graph.addVertex(a));
assertEquals(true, graph.addVertex(b));
assertEquals(true, graph.addVertex(c));
assertEquals(true, graph.addVertex(d));
assertEquals(true, graph.addVertex(e));
assertEquals(true, graph.addVertex(f));
assertEquals(true, graph.addVertex(g));
assertEquals(true, graph.addVertex(h));
assertEquals(true, graph.addVertex(i));
assertEquals(true, graph.addVertex(j));
assertEquals(0, graph.addEdge(a, b));
assertEquals(0, graph.addEdge(a, d));
assertEquals(0, graph.addEdge(b, d));
assertEquals(0, graph.addEdge(c, d));
assertEquals(0, graph.addEdge(d, e));
assertEquals(0, graph.addEdge(c, f));
assertEquals(0, graph.addEdge(e, g));
assertEquals(0, graph.addEdge(f, g));
assertEquals(0, graph.addEdge(h, i));
assertEquals(0, graph.addEdge(i, j));
assertEquals(2, graph.getDistance(a, e));
assertEquals(1, graph.getDistance(a, d));
assertEquals(3, graph.getDistance(a, g));
assertEquals(3, graph.getDistance(b, f));
assertEquals(2, graph.getDistance(d, f));
assertEquals(2, graph.getDistance(h, j));
assertEquals(0, graph.getDistance(i, i));
assertEquals(-1, graph.getDistance(d, j));
assertEquals(-1, graph.getDistance(c, i));
assertEquals(-1, graph.getDistance(f, h));
}
}
思路 / 简化版实现方案
问题简述:
设计一款棋类游戏,同时支持国际象棋(Chess)和围棋(Go)。实现功能:
接口Game由chessGame和goGame实现,是Main()程序通向游戏对象的路口,通过一个接口把两种游戏分开,相同的操作类型在不同游戏中实现。
Game拥有7种操作所对应的方法,并且能支持访问下属的Player(先后手访问、名字访问)和Board,以及为玩家产生所对应Piece的功能。
7种操作除了“end”均隶属于Player对象进行操作,其中的“放棋”、“移动”、“吃子/提子”均在Action接口的实现类中完成,在Game接口的实现类中判断是否执行成功即可。因此3种操作可以在Game的实现类中实现近乎标准化和统一(输入操作类型String即可),以“吃子/提子”(capture)为例:
@Override
public boolean capture(Player player, Position... positions) {
if (player == null)
return false;
return player.doAction("capture", null, positions) != null;
}
在两种游戏中,差异较大的之一就是棋子。棋子属于玩家,但棋子是由一个特定类型的游戏所“产生”的,因此Game的两个实现类中差异最大的就是产生棋子的方法:
在chess中,黑白双方棋子除了颜色都相同,因此可以用chessGame静态成员变量预设好每个棋子的名字、数量和位置(黑白双方可以用公式颠倒)。然后依据预设的静态数据新建16个Piece对象,初始化Position、Player,最后加入Set中返回。goGame中大致相同。
/**
* the Map whose keys are the name of pieces, values are the numbers they are on board totally
*/
private static final Map<String, Integer> piecesSumMap = new HashMap<String, Integer>() {
private static final long serialVersionUID = 1L;
{
put("P", 8);
put("R", 2);
put("N", 2);
put("B", 2);
put("Q", 1);
put("K", 1);
}
};
/**
* the Map whose keys are the name of pieces, values are the coordinates of them
*/
private static final Map<String, int[][]> piecesPosMap = new HashMap<String, int[][]>() {
private static final long serialVersionUID = 1L;
{
put("P", new int[][] { { 0, 1, 2, 3, 4, 5, 6, 7 }, { 1, 1, 1, 1, 1, 1, 1, 1 } });
put("R", new int[][] { { 0, 7 }, { 0, 0 } });
put("N", new int[][] { { 1, 6 }, { 0, 0 } });
put("B", new int[][] { { 2, 5 }, { 0, 0 } });
put("Q", new int[][] { { 3 }, { 0 } });
put("K", new int[][] { { 4 }, { 0 } });
}
};
@Override
public Set<Piece> pieces(boolean firstFlag) {
Set<Piece> pieces = new HashSet<Piece>();
for (Map.Entry<String, Integer> entry : piecesSumMap.entrySet()) {
for (int i = 0; i < entry.getValue(); i++) {
String pieceName = (firstFlag ? "W" : "B") + entry.getKey() + i; // eg. WB1 BR2 WP3
Piece piece = new Piece(pieceName, firstFlag, (firstFlag ? player1 : player2));
// get the coordinate of a specific piece
int[] X = piecesPosMap.get(entry.getKey())[0];
int[] Y = piecesPosMap.get(entry.getKey())[1];
int x = X[i], y = (firstFlag ? Y[i] : CHESS_BOARD_SIDE - Y[i] - 1);
// put the piece on the position
piece.modifyPositionAs(board.positionXY(x, y));
board.positionXY(x, y).modifyPieceAs(piece);
// add the piece into the piece set of the player
pieces.add(piece);
}
}
return pieces;
}
Game是Board和Player的父类。Board的创建只能源于Game的构造函数,Player的创建必须后于Game且玩家的Piece依赖于Game的函数。
上图上方三个分别是Game和Game的实现类chessGame和goGame,Board和Game隶属于Game,在不同情况下调用两种实现类,且这两者无法联系,保护了对象的私有数据。
Board是棋盘的对象,构造依赖于Game的构造。Position的创建也依赖于Board,Board也存储这二维Position类型数组,并且拥有final变量N记录棋盘的边长。
Board的主要用于查询指定位置的Position、Piece和Player,以及打印棋盘。查询Position可以直接访问positions成员变量,而查询Piece又要访问指定位置的Position不为空的Piece,而查询Player又要查询不空的Piece的Player。
在三个函数的实现中,按照调用关系,先后实现。在这里设计Position和Piece平级且捆绑,同为可变。
/**
* ask object of the position
* @param x the x of the asking position
* @param y the y of the asking position
* @return object of Position of the (x, y)
*/
public Position positionXY(int x, int y) {
if (x < 0 || x >= this.N || y < 0 || y >= this.N)
return null;
return board[x][y];
}
/**
* ask the piece on (x, y) if there isn't null
* @param x the x of the asking position
* @param y the y of the asking position
* @return object of Piece of the (x, y)
*/
public Piece pieceXY(int x, int y) {
if (positionXY(x, y) == null)
return null;
return positionXY(x, y).piece();
}
/**
* ask the player who owns the piece of (x, y) or null if not
* @param x the x of the asking position
* @param y the y of the asking position
* @return Player if (x, y) is occupied, null if it's free
*/
public Player XYisFree(int x, int y) {
if (pieceXY(x, y) == null)
return null;
return pieceXY(x, y).player();
}
/**
* print the Board
* '.' if one position has no piece
* or the piece' name if not
*/
public void printBoard() {
for (int i = 0; i < this.N; i++) {
for (int j = 0; j < this.N; j++) {
if (this.pieceXY(i, j) != null) {
if (game.gameType().equals("chess")) {
/*
* the capital letter represents the white piece
* the little letter represents the black piece
*/
System.out.print((this.pieceXY(i, j).isFirst() ? this.pieceXY(i, j).name().charAt(1)
: this.pieceXY(i, j).name().toLowerCase().charAt(1)) + " ");
} else if (game.gameType().equals("go")) {
/*
* the 'B' represents the black pieces
* the 'W' represents the white pieces
*/
System.out.print(this.pieceXY(i, j).name().charAt(0) + " ");
}
} else {
// if there is no piece
System.out.print(". ");
}
}
System.out.println();
}
}
国际象棋:大写代表白方,小写代表黑方。
围棋:B代表黑方,W代表白方。
棋盘能够管理棋格/点,而根据要求棋盘是不能管理棋子的。因此Board是Game的子类,也是Position的父类。
Player对象代表着玩家,有这Boolean标签区分先后手,拥有Piece并管理Action。
Player的方法除了查询本对象的信息,还有寻找自己下属的棋子,以及执行并记录Action。
寻找棋子时:当以棋子名字查询时,只需在成员变量Set pieces中遍历,判断棋子的名字是否相等即可;而当查询任意一个空闲棋子时,则需判断其position是否为空。此外,Player还能计算本方棋盘上棋子总数。
/**
* ask the number of pieces
* @return the number of pieces
*/
public int sumPiece() {
int sum = 0;
for (Piece piece : pieces) {
// calculate the non-null piece
if (piece.position() != null) {
sum++;
}
}
return sum;
}
/**
* ask any of the free pieces
* @return a free piece belonging to the player
*/
public Piece freePiece() {
for (Piece piece : this.pieces) {
// find a random free piece
if (piece.position() == null)
return piece;
}
return null;
}
/**
* find a piece which owns the same name
* @param pieceName String of the name of the piece
* @return piece object of the piece name
*/
public Piece findPieceByName(String pieceName) {
for (Piece piece : this.pieces) {
// find the piece whose name is matched with the giving
if (piece.name().equals(pieceName))
return piece;
}
return null;
}
在执行Action的方法中,通过构造一个新的Action对象来执行。在Action对象的内部会进行完整的操作,玩家只需访问改动作对象的成功与否,然后加入List存储即可。
/**
* generate a new action and init the action type
* @param actionType String of the type of the action
* @param piece the putting piece when the actionType is "put", null if not
* @param positions the positions related to the action
* @return the object of the action created
*/
public Action doAction(String actionType, Piece piece, Position... positions) {
if (!actionTypes.contains(actionType))
return null;
Action action = Action.newAction(this.game.gameType(), this, actionType, piece, positions);
if (action.askSuccess())
actions.add(action);
else
action = null;
return action;
}
Position代表着国际象棋棋盘的格子以及围棋棋盘的交叉点。
Position隶属于Board,一个对象的x和y是不可变的,但Position记录的Piece对象是可变的,提供了方法进行修改。
/**
* to update the Piece of the Position
* @param newPiece the new piece that is to modify it as
* @return true if the Piece updated successfully, false if the new Piece is null
*/
public boolean modifyPieceAs(Piece newPiece) {
this.piece = newPiece;
checkRep();
return true;
}
Piece代表着棋盘上的棋子,用一个唯一标识的String来区分每一个棋子,和一个Boolean来区分先后手,这两个信息是不可变的。
和Position相似,Piece也提供了修改Position的方法,用于移动棋子等变化操作。
/**
* to update the position of the piece
* @param newPosition the new position that is to modify it as
* @return true if the position updated successfully, false if the newPosition is null
*/
public boolean modifyPositionAs(Position newPosition) {
this.position = newPosition;
checkRep();
return true;
}
Action代表着要求里的若干个操作,通过一个String来区分,在构造函数中就实现这一操作(主要是放棋、吃子/提子、移动)。所需要的参数通过客户端的输入,再由Game传递,Player的方法doAction()中构造,然后在Action对象中执行,并记录执行的成败。Action是基于Player实现的,作用于Position。
接口Action有一个静态函数作为构造器:
/**
* generate a new Action of one piece
* do the action
* @param gameType String of the type of the game
* @param player the acting player
* @param actionType String of the type of the action
* @param piece the operating piece
* @param positions the positions related to the action
* @return an object of a type of Action(chessAction or goAction)
*/
public static Action newAction(String gameType, Player player, String actionType, Piece piece,
Position... positions) {
return gameType.equals("chess") ? (new chessAction(player, actionType, piece, positions))
: (new goAction(player, actionType, piece, positions));
}
创建新的对象后,依据String actionType执行操作:
/**
* create and finish the action
* @param player the operating player
* @param actionType String of the type of the action
* @param piece the operating piece
* @param positions the position related to the action
*/
chessAction(Player player, String actionType, Piece piece, Position... positions) {
this.player = player;
this.positions = positions;
this.piece = piece;
this.actionType = actionType;
switch (actionType) {
case "put":
this.actionSuccess = (piece != null) && put();
break;
case "move":
this.actionSuccess = move();
break;
case "capture":
this.actionSuccess = capture();
break;
case "AskIsFree":
this.actionSuccess = true;
break;
case "SumPiece":
this.actionSuccess = true;
break;
case "skip":
this.actionSuccess = true;
break;
default:
this.actionSuccess = false;
}
checkRep();
}
chessAction和goAction分别重写Action中的操作,主要是put()、move()、capture()三个方法。最后通过方法的返回值将结果记录在actionSuccess。
chessAction代表着国际象棋中的操作。
put()操作与go类型,将在goAction中举例。move()操作和capture()操作中,国际象棋比围棋多需要1个Position操作,因此输入Position时就以不定项参数输入。两个操作大体类似,主要区别于:move的目标格要求为空,capture需要移除目标格棋子。以capture()为例:
public boolean capture() {
Position source = this.positions[0], target = this.positions[1];
// capture requirement: 1. the target can't be null 2. the source can't be null
// 3. the target must belong to the OPPOSITE 4. the source must belong to this player
if (target.piece() != null && source.piece() != null && (!target.piece().player().equals(player))
&& source.piece().player().equals(player)) {
target.piece().modifyPositionAs(null); // the piece capturing removed
source.piece().modifyPositionAs(target); // captured piece move to the target
target.modifyPieceAs(source.piece());// move the piece, this must be done before source's piece be null
source.modifyPieceAs(null);// set the source null
return true;
}
return false;
}
goAction代表着围棋中的操作。
put()操作在国际象棋和围棋中类似:客户端输入棋的名字(类型即可、无编号),然后game寻找该棋子后输入action对象;board将(x, y)转换为Position对象后输入action对象;最后执行:(以goAction为例)
@Override
public boolean put() {
Position target = this.positions[0];
// put requirement:
// 1. the piece of the target can't be null
// 2. the putting piece can't be null
// 3. the piece must belong to the player
if (this.piece.position() == null && target.piece() == null && player.pieces().contains(piece)) {
this.piece.modifyPositionAs(target);
target.modifyPieceAs(this.piece);
return true;
}
return false;
}
Main()主要分为3个步骤:
/**
* provide 2 choices on screen for users to choose chess or go.
* generate new Game;
* generate new Board;
*
* get 2 players' names printed on the screen.
* generate 2 new Player;
* generate new Piece belonged to Player;
*
* @param args FORMAT
*/
public static void main(String[] args) {
// scan : 3 String
System.out.println("Please choose a type of game (chess/go):");
String gameType = input.nextLine();
System.out.println("Please write the player1's name (First):");
String playerName1 = input.nextLine();
System.out.println("Please write the player2's name (Later):");
String playerName2 = input.nextLine();
// new objects
final Game game = Game.newGame(gameType);
final Player player1 = new Player(game, playerName1, true);
final Player player2 = new Player(game, playerName2, false);
game.setPlayers(player1, player2);
player1.pieces = game.pieces(true);
player2.pieces = game.pieces(false);
// gaming
GAME(game);
input.close();
// actions
printRecord(game, player1, player2);
System.out.println("That's All! See You Next Time!");
}
/**
* provide 7 choices including;
* 1. to put a piece : put();
* 2. to move a piece : move();
* 3. to capture particular piece(s) : capture();
* 4. ask whether a position is free or not : isFree();
* 5. calculate the sum of the pieces on the
* board : sumPiece();
* 6. skip : skip()
* 7. print "end" : end()
* @param game the object of the game
*/
private static void GAME(Game game) {
boolean endFlag = false;
System.out.println("Game Start!");
while (!endFlag) {
System.out.println("Please choose a player:");
String playerName = input.next();
endFlag = playerActing(game, game.choosePlayerByName(playerName));
game.board().printBoard();
}
}
/**
* The chosen player is operating one choice
* @param game the object of the game
* @param player the object of the operating player
* @return true if the player choose ending the game, false if not
*/
private static boolean playerActing(Game game, Player player) {
// menu
System.out.println("Please choose an action type:");
System.out.println("1. put");
System.out.println(game.gameType().equals("chess") ? "2. move" : "");
System.out.println("3. capture");
System.out.println("4. ask: (x, y) is free?");
System.out.println("5. ask: sum of both players' pieces");
System.out.println("6. skip the choose");
System.out.println("7. end the game");
// input information
String pieceName = "";
int x1, y1; // source
int x2, y2; // target
// catch choice
System.out.print("Your Choice is:");
int choice = input.nextInt();
while (choice > 0 && choice <= 7) { // prepare the probable wrong choice
switch (choice) {
case 1: // put
if (game.gameType().equals("chess")) {
pieceName = input.next("Piece Name (eg. WQ0 BP2): ");
}
System.out.print("The (x, y) of the target: ");
x1 = input.nextInt();
y1 = input.nextInt();
// choose a piece freely if go
// get the particular piece if chess
Piece puttingPiece = game.gameType().equals("chess") ? player.findPieceByName(pieceName)
: player.freePiece();
// print result
System.out.println(game.put(player, puttingPiece, game.board().positionXY(x1, y1)));
return false;
case 2: // move
System.out.print("The (x, y) of both source and target: ");
x1 = input.nextInt();
y1 = input.nextInt();
x2 = input.nextInt();
y2 = input.nextInt();
System.out.println(
game.move(player, game.board().positionXY(x1, y1), game.board().positionXY(x2, y2)));
return false;
case 3: // capture
if (game.gameType().equals("chess")) {
System.out.print("The (x, y) of both source and target: ");
x1 = input.nextInt();
y1 = input.nextInt();
x2 = input.nextInt();
y2 = input.nextInt();
System.out.println(
game.capture(player, game.board().positionXY(x1, y1), game.board().positionXY(x2, y2)));
} else if (game.gameType().equals("go")) {
System.out.print("The (x, y) of the target: ");
x1 = input.nextInt();
y1 = input.nextInt();
System.out.println(game.capture(player, game.board().positionXY(x1, y1)));
}
return false;
case 4: // is free?
System.out.print("The (x, y) of the questioning grid: ");
x1 = input.nextInt();
y1 = input.nextInt();
Player here = game.isFree(player, x1, y1);
System.out.println(here == null ? "Free" : here.name());
return false;
case 5: // sum of pieces
Map<Player, Integer> sumPiece = game.sumPiece(player); // two players' sum of pieces
System.out.println(game.player1().name() + ":" + sumPiece.get(game.player1()) + " pieces");
System.out.println(game.player2().name() + ":" + sumPiece.get(game.player2()) + " pieces");
return false;
case 6: // skip
game.skip(player);
System.out.println("Skip");
return false;
case 7: // end
game.end();
System.out.println("The Game is ended.");
return true;
}
System.out.println("Input WRONG, Please choose again:");
}
return false;
}
/**
* after the game is ended, to print both players' records of the game.
* @param game the object of the game
* @param player1 the object of the first hand player
* @param player2 the object of the later hand player
*/
private static void printRecord(Game game, Player player1, Player player2) {
System.out.println("\nAll of the Actions Record are followed.");
// get the record of the actions
List<Action> actions1 = player1.actions();
List<Action> actions2 = player2.actions();
System.out.println("\n" + player1.name() + "'s Actions:");
// print their action types
for (int i = 0; i < actions1.size(); i++) {
if (actions1.get(i) != null)
System.out.println(i + ": " + actions1.get(i).actionType());
}
System.out.println("\n" + player2.name() + "'s Actions:");
for (int i = 0; i < actions2.size(); i++) {
if (actions2.get(i) != null)
System.out.println(i + ": " + actions2.get(i).actionType());
}
}
分别测试国际象棋和围棋,测试类分别为ChessTest和GoTest。由于国际象棋的测试比围棋困难,约束也比围棋多,因此设计ChessTest后进行一定更改就可以测试围棋。以下以chess为例,设计ADT测试方案。
测试初始化游戏的操作:新建指定类型的game、board,新建指定名字的player,以及新建piece并将棋子赋予player。
测试game、player、piece均不为空。
@Test
public void testInit() {
final Game game = Game.newGame("chess");
assertNotEquals(null, game);
final Player player1 = new Player(game, "p1", true);
assertNotEquals(null, player1);
final Player player2 = new Player(game, "p2", false);
assertNotEquals(null, player2);
assertEquals(true, game.setPlayers(player1, player2));
player1.pieces = game.pieces(true);
assertNotEquals(Collections.EMPTY_SET, player1.pieces());
player2.pieces = game.pieces(false);
assertNotEquals(Collections.EMPTY_SET, player2.pieces());
}
测试棋子是否被新建,以及是否赋予玩家、初始化位置。
@Test
public void testPiece() {
// init
final Game game = Game.newGame("chess");
final Player player1 = new Player(game, "p1", true);
final Player player2 = new Player(game, "p2", false);
game.setPlayers(player1, player2);
player1.pieces = game.pieces(true);
player2.pieces = game.pieces(false);
// Piece.modifyAsPosition
for (Piece piece : player1.pieces()) {
assertNotNull(piece.position());
assertNotNull(piece.position().piece());
assertSame(piece, piece.position().piece());
}
}
测试棋盘的方法。
@Test
public void testBoard() {
// init
final Game game = Game.newGame("chess");
final Player player1 = new Player(game, "p1", true);
final Player player2 = new Player(game, "p2", false);
game.setPlayers(player1, player2);
player1.pieces = game.pieces(true);
player2.pieces = game.pieces(false);
// Board.printBoard()
assertTrue(game.move(player1, game.board().positionXY(0, 1), game.board().positionXY(0, 3)));
assertTrue(game.capture(player2, game.board().positionXY(0, 6), game.board().positionXY(0, 3)));
game.board().printBoard();
// Board.PositionXY(x, y)
assertNotNull(game.board().positionXY(0, 1));
assertNotNull(game.board().positionXY(4, 6));
assertEquals("BP4", game.board().positionXY(4, 6).piece().name());
assertNull(game.board().positionXY(3, 3).piece());
// Board.pieceXY(x, y)
assertEquals("WR0", game.board().pieceXY(0, 0).name());
assertEquals("BP5", game.board().pieceXY(5, 6).name());
// Board.XYisFree(x, y)
assertEquals(null, game.board().XYisFree(4, 5));
assertEquals(player1, game.board().XYisFree(1, 1));
assertEquals(player2, game.board().XYisFree(7, 6));
}
测试位置的方法。
@Test
public void testPosition() {
// init
final Game game = Game.newGame("chess");
final Player player1 = new Player(game, "p1", true);
final Player player2 = new Player(game, "p2", false);
game.setPlayers(player1, player2);
player1.pieces = game.pieces(true);
player2.pieces = game.pieces(false);
// Position.piece()
assertEquals("BQ0", game.board().positionXY(3, 7).piece().name());
assertEquals("WK0", game.board().positionXY(4, 0).piece().name());
// Position.x()
assertEquals(4, game.board().positionXY(4, 0).x());
// Position.y()
assertEquals(5, game.board().positionXY(2, 5).y());
// Position.player()
assertEquals(player2, game.board().positionXY(6, 6).player());
// Position.modify
assertEquals(player1, game.board().XYisFree(1, 1));
assertEquals(true, game.board().positionXY(1, 1).modifyPieceAs(null));
assertNull(game.board().XYisFree(1, 1));
}
测试玩家。
@Test
public void testPlayer() {
// init
final Game game = Game.newGame("chess");
final Player player1 = new Player(game, "p1", true);
final Player player2 = new Player(game, "p2", false);
game.setPlayers(player1, player2);
player1.pieces = game.pieces(true);
player2.pieces = game.pieces(false);
// Player.isFirst()
assertEquals(true, player1.isFirst());
assertEquals(false, player2.isFirst());
// Player.game()
assertEquals(game, player2.game());
// Player.pieces()
for (int i = 0; i < game.board().boardLength(); i++) {
for (int j = 0; j < game.board().boardLength(); j++) {
if (game.board().pieceXY(i, j) != null)
assertEquals(true, player1.pieces().contains(game.board().pieceXY(i, j))
|| player2.pieces().contains(game.board().pieceXY(i, j)));
}
}
}
测试放棋操作。
@Test
public void testPut() {
// init
final Game game = Game.newGame("chess");
final Player player1 = new Player(game, "p1", true);
final Player player2 = new Player(game, "p2", false);
game.setPlayers(player1, player2);
player1.pieces = game.pieces(true);
player2.pieces = game.pieces(false);
// put
assertEquals(false, game.put(player1, player1.findPieceByName("WP0"), game.board().positionXY(0, 1)));
assertEquals(false, game.put(player2, player2.findPieceByName("BN1"), game.board().positionXY(5, 4)));
// After capture
assertTrue(game.capture(player2, game.board().positionXY(0, 6), game.board().positionXY(0, 1)));
assertTrue(game.put(player1, player1.findPieceByName("WP0"), game.board().positionXY(0, 6)));
}
测试移动棋子操作。
@Test
public void testMove() {
// init
final Game game = Game.newGame("chess");
final Player player1 = new Player(game, "p1", true);
final Player player2 = new Player(game, "p2", false);
game.setPlayers(player1, player2);
player1.pieces = game.pieces(true);
player2.pieces = game.pieces(false);
// move
assertEquals(true, game.move(player1, game.board().positionXY(0, 1), game.board().positionXY(0, 3)));
assertNull(game.board().positionXY(0, 1).piece());
assertNotNull(game.board().positionXY(0, 3).piece());
assertEquals(false, game.move(player1, game.board().positionXY(0, 1), game.board().positionXY(0, 4)));
assertEquals(false, game.move(player2, game.board().positionXY(6, 0), game.board().positionXY(5, 2)));
assertEquals(true, game.move(player1, game.board().positionXY(1, 0), game.board().positionXY(2, 2)));
assertEquals(false, game.move(player1, game.board().positionXY(1, 0), game.board().positionXY(0, 2)));
assertNull(game.board().positionXY(0, 2).piece());
assertNull(game.board().positionXY(1, 0).piece());
assertEquals(false, game.move(player2, game.board().positionXY(3, 7), game.board().positionXY(4, 7)));
assertEquals(false, game.move(player1, game.board().positionXY(2, 2), game.board().positionXY(4, 1)));
assertNotNull(game.board().positionXY(4, 1).piece());
assertNotNull(game.board().positionXY(2, 2).piece());
assertEquals(true, game.move(player2, game.board().positionXY(0, 6), game.board().positionXY(0, 1)));
assertEquals(player2, game.board().positionXY(0, 1).piece().player());
assertEquals("BP0", game.board().positionXY(0, 1).piece().name());
assertEquals(player1, game.board().positionXY(0, 3).piece().player());
// sumPiece
assertEquals(16, player2.sumPiece());
}
结合put操作测试capture吃子操作。
@Test
public void testCaptureAndPut() {
// init
final Game game = Game.newGame("chess");
final Player player1 = new Player(game, "p1", true);
final Player player2 = new Player(game, "p2", false);
game.setPlayers(player1, player2);
player1.pieces = game.pieces(true);
player2.pieces = game.pieces(false);
// capture
assertEquals(true, game.capture(player1, game.board().positionXY(0, 1), game.board().positionXY(0, 6)));
assertEquals(false, game.capture(player1, game.board().positionXY(1, 1), game.board().positionXY(2, 1)));
assertEquals(false, game.capture(player1, game.board().positionXY(1, 1), game.board().positionXY(1, 3)));
assertEquals(true, game.capture(player1, game.board().positionXY(0, 6), game.board().positionXY(1, 6)));
assertEquals(false, game.capture(player1, game.board().positionXY(1, 1), game.board().positionXY(1, 6)));
assertEquals("WP0", game.board().pieceXY(1, 6).name());
assertSame(player1.findPieceByName("WP0"), game.board().pieceXY(1, 6));
assertEquals(true, game.capture(player1, game.board().positionXY(2, 1), game.board().positionXY(2, 6)));
assertEquals(13, player2.sumPiece());
// put
assertEquals("BP1", player2.findPieceByName("BP1").name());
assertNull(game.board().positionXY(0, 4).piece());
assertTrue(game.put(player2, player2.findPieceByName("BP0"), game.board().positionXY(0, 4)));
assertFalse(game.put(player2, player2.findPieceByName("BP1"), game.board().positionXY(0, 4)));
assertFalse(game.put(player1, player2.findPieceByName("BP1"), game.board().positionXY(0, 4)));
assertFalse(game.put(player2, player2.findPieceByName("BP0"), game.board().positionXY(0, 4)));
assertNull(player1.findPieceByName("BP1"));
assertFalse(game.put(player2, player1.findPieceByName("BP1"), game.board().positionXY(1, 4)));
assertNull(game.board().positionXY(8, -1));
assertFalse(game.put(player2, player2.findPieceByName("BP2"), game.board().positionXY(8, -1)));
}
日期 | 时间段 | 计划任务 | 实际完成情况 |
---|---|---|---|
2020-03-09 | 晚上 | 初始化项目 | 完成 |
2020-03-10 | 中午 | Problem1 3.1.1-3.1.2 | 完成 |
2020-03-10 | 晚上 | Problem1 3.1.3.1 Edge Graph | 完成 |
2020-03-11 | 晚上 | Problem1 3.1.3.2 Vertex Graph | 完成 |
2020-03-12 | 下午 | 通过Graph Instance Test | 完成 |
2020-03-12 | 晚上 | Problem1 3.1.5 Graph Poet | 完成 |
2020-03-13 | 上午 | Problem1 3.1.5 Test | 完成 |
2020-03-13 | 上午 | Problem2 3.2整体完成 | 完成 |
2020-03-13 | 晚上 | Problem3 设计框架 写AF&RI | 完成 |
2020-03-14 | 上午 | Problem3 完成框架 | 完成 |
2020-03-14 | 下午 | 实现Action接口、Player、Board、Position、Piece具体功能 | 完成 |
2020-03-14 | 晚上 | 完善上述功能,修改bug | 完成 |
2020-03-15 | 上午 | 实现Game接口、chessAction、goAction功能 | 完成 |
2020-03-15 | 晚上 | 实现chessGame、goGame功能,调试测试用例 | 完成 |
2020-03-16 | 晚上 | 通过chessGame测试 | 完成 |
2020-03-17 | 下午 | 通过goGame测试 | 完成 |
2020-03-17 | 晚上 | 验收完成 | 完成 |
本项目于3.17日实验课验收,请放心参考
参考时文中有给出一些建议,请查看
基本更新完成