所以我有可以运行的代码,但是我需要为其中一个类(单元格)编写测试,我不知道该怎么做。
通常,我会在 CellsTest 的开头有一个帮助类,它会封装构造函数调用,它依赖于实现整个程序的类的特定类名。这个助手类将返回该程序的一个实例。
所以,我认为我需要有类似的东西:
private Maze createMaze() {
return new Maze();
}
但是,我的测试方法无法访问 Cell 方法,只能访问 Maze 方法......所以我猜这是错误的,我不确定我应该如何实际执行此操作。
//Maze.java
package falstad;
import java.awt.*;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Class handles the user interaction for the maze.
*/
//public class Maze extends Panel {
public class Maze {
final private ArrayList<Viewer> views = new ArrayList<Viewer>() ;
MazePanel panel ; // graphics to draw on, shared by all views
private int state;
private int percentdone = 0;
private boolean showMaze;
private boolean showSolution;
private boolean solving;
private boolean mapMode;
//static final int viewz = 50;
int viewx, viewy, angle;
int dx, dy;
int px, py ;
int walkStep;
int viewdx, viewdy;
boolean deepdebug = false;
boolean allVisible = false;
boolean newGame = false;
int mazew; // width
int mazeh; // height
Cells mazecells ;
Distance mazedists ;
Cells seencells ;
BSPNode rootnode ;
// Mazebuilder is used to calculate a new maze together with a solution
// The maze is computed in a separate thread. It is started in the local Build method.
MazeBuilder mazebuilder;
final int ESCAPE = 27;
int method = 0 ; // 0 : default method, Falstad's original code
int zscale = Constants.VIEW_HEIGHT/2;
private RangeSet rset;
//Constructor
public Maze() {
super() ;
panel = new MazePanel() ;
}
//selects a generation method
public Maze(int method)
{
super() ;
if (1 == method)
this.method = 1 ;
panel = new MazePanel() ;
}
public void init() {
state = Constants.STATE_TITLE;
rset = new RangeSet();
panel.initBufferImage() ;
addView(new MazeView(this)) ;
notifyViewerRedraw() ;
}
// Method obtains a new Mazebuilder and computes new maze
private void build(int skill) {
state = Constants.STATE_GENERATING;
percentdone = 0;
notifyViewerRedraw() ;
// select generation method
switch(method){
case 1 : mazebuilder = new MazeBuilderPrim();
break ;
case 0:
default : mazebuilder = new MazeBuilder();
break ;
}
mazew = Constants.SKILL_X[skill];
mazeh = Constants.SKILL_Y[skill];
mazebuilder.build(this, mazew, mazeh, Constants.SKILL_ROOMS[skill], Constants.SKILL_PARTCT[skill]);
}
//Call back method for MazeBuilder to communicate newly generated maze
public void newMaze(BSPNode root, Cells c, Distance dists, int startx, int starty) {
if (Cells.deepdebugWall)
{
c.saveLogFile(Cells.deepedebugWallFileName);
}
showMaze = showSolution = solving = false;
mazecells = c ;
mazedists = dists;
seencells = new Cells(mazew+1,mazeh+1) ;
rootnode = root ;
setCurrentDirection(1, 0) ;
setCurrentPosition(startx,starty) ;
walkStep = 0;
viewdx = dx<<16;
viewdy = dy<<16;
angle = 0;
mapMode = false;
state = Constants.STATE_PLAY;
cleanViews() ;
addView(new FirstPersonDrawer(Constants.VIEW_WIDTH,Constants.VIEW_HEIGHT,
Constants.MAP_UNIT,Constants.STEP_SIZE, mazecells, seencells, 10, mazedists.getDists(), mazew, mazeh, root, this)) ;
addView(new MapDrawer(Constants.VIEW_WIDTH,Constants.VIEW_HEIGHT,Constants.MAP_UNIT,Constants.STEP_SIZE, mazecells, seencells, 10, mazedists.getDists(), mazew, mazeh, this)) ;
notifyViewerRedraw() ;
}
public void addView(Viewer view) {
views.add(view) ;
}
public void removeView(Viewer view) {
views.remove(view) ;
}
private void cleanViews() {
Iterator<Viewer> it = views.iterator() ;
while (it.hasNext())
{
Viewer v = it.next() ;
if ((v instanceof FirstPersonDrawer)||(v instanceof MapDrawer))
{
//System.out.println("Removing " + v);
it.remove() ;
}
}
}
private void notifyViewerRedraw() {
Iterator<Viewer> it = views.iterator() ;
while (it.hasNext())
{
Viewer v = it.next() ;
v.redraw(panel.getBufferGraphics(), state, px, py, viewdx, viewdy, walkStep, Constants.VIEW_OFFSET, rset, angle) ;
}
panel.update() ;
}
private void notifyViewerIncrementMapScale() {
Iterator<Viewer> it = views.iterator() ;
while (it.hasNext())
{
Viewer v = it.next() ;
v.incrementMapScale() ;
}
panel.update() ;
}
private void notifyViewerDecrementMapScale() {
Iterator<Viewer> it = views.iterator() ;
while (it.hasNext())
{
Viewer v = it.next() ;
v.decrementMapScale() ;
}
panel.update() ;
}
boolean isInMapMode() {
return mapMode ;
}
boolean isInShowMazeMode() {
return showMaze ;
}
boolean isInShowSolutionMode() {
return showSolution ;
}
public String getPercentDone(){
return String.valueOf(percentdone) ;
}
public Panel getPanel() {
return panel ;
}
private void setCurrentPosition(int x, int y)
{
px = x ;
py = y ;
}
private void setCurrentDirection(int x, int y)
{
dx = x ;
dy = y ;
}
void buildInterrupted() {
state = Constants.STATE_TITLE;
notifyViewerRedraw() ;
mazebuilder = null;
}
final double radify(int x) {
return x*Math.PI/180;
}
public boolean increasePercentage(int pc) {
if (percentdone < pc && pc < 100) {
percentdone = pc;
if (state == Constants.STATE_GENERATING)
{
notifyViewerRedraw() ;
}
else
dbg("Warning: Receiving update request for increasePercentage while not in generating state, skip redraw.") ;
return true ;
}
return false ;
}
private void dbg(String str) {
//System.out.println(str);
}
private void logPosition() {
if (!deepdebug)
return;
dbg("x="+viewx/Constants.MAP_UNIT+" ("+
viewx+") y="+viewy/Constants.MAP_UNIT+" ("+viewy+") ang="+
angle+" dx="+dx+" dy="+dy+" "+viewdx+" "+viewdy);
}
private boolean checkMove(int dir) {
int a = angle/90;
if (dir == -1)
a = (a+2) & 3;
return mazecells.hasMaskedBitsFalse(px, py, Constants.MASKS[a]) ;
}
private void rotateStep() {
angle = (angle+1800) % 360;
viewdx = (int) (Math.cos(radify(angle))*(1<<16));
viewdy = (int) (Math.sin(radify(angle))*(1<<16));
moveStep();
}
private void moveStep() {
notifyViewerRedraw() ;
try {
Thread.currentThread().sleep(25);
} catch (Exception e) { }
}
private void rotateFinish() {
setCurrentDirection((int) Math.cos(radify(angle)), (int) Math.sin(radify(angle))) ;
logPosition();
}
private void walkFinish(int dir) {
setCurrentPosition(px + dir*dx, py + dir*dy) ;
if (isEndPosition(px,py)) {
state = Constants.STATE_FINISH;
notifyViewerRedraw() ;
}
walkStep = 0;
logPosition();
}
private boolean isEndPosition(int x, int y) {
return x < 0 || y < 0 || x >= mazew || y >= mazeh;
}
synchronized private void walk(int dir) {
if (!checkMove(dir))
return;
for (int step = 0; step != 4; step++) {
walkStep += dir;
moveStep();
}
walkFinish(dir);
}
synchronized private void rotate(int dir) {
final int originalAngle = angle;
final int steps = 4;
for (int i = 0; i != steps; i++) {
angle = originalAngle + dir*(90*(i+1))/steps;
rotateStep();
}
rotateFinish();
}
//CLASS CONTROLLING PLAYER MOVEMENTS, REMOVED FOR CHARACTER COUNT
}
}
//Cells.java
package falstad;
import java.io.BufferedWriter;
import java.io.FileWriter;
//This class encapsulates all access to a grid of cells
public class Cells {
private int width;
private int height ;
private int[][] cells;
public Cells(int w, int h) {
width = w ;
height = h ;
cells = new int[w][h];
}
public Cells(int[][] target){
this(target.length, target[0].length);
for (int i=0; i<width; i++)
for (int j=0; j<height; j++)
this.cells[i][j]=target[i][j];
}
public int getCells( int x, int y )
{
return cells[x][y] ;
}
static public int[] getMasks() {
return Constants.MASKS ;
}
public boolean canGo(int x, int y, int dx, int dy) {
if (hasMaskedBitsTrue(x, y, (getBit(dx, dy) << Constants.CW_BOUND_SHIFT)))
return false;
return isFirstVisit(x+dx, y+dy);
}
private int getBit(int dx, int dy) {
int bit = 0;
switch (dx + dy * 2) {
case 1: bit = Constants.CW_RIGHT; break;
case -1: bit = Constants.CW_LEFT; break;
case 2: bit = Constants.CW_BOT; break;
case -2: bit = Constants.CW_TOP; break;
default: dbg("getBit problem "+dx+" "+dy); break;
}
return bit;
}
//CLASSES FOR BITWISE ADJUSTMENTS, REMOVED FOR CHARACTER COUNT
public void initialize() {
int x, y;
for (x = 0; x != width; x++) {
for (y = 0; y != height; y++) {
setBitToOne(x, y, (Constants.CW_VISITED | Constants.CW_ALL));
}
setBitToOne(x, 0, Constants.CW_TOP_BOUND);
setBitToOne(x, height-1, Constants.CW_BOT_BOUND);
}
for (y = 0; y != height; y++) {
setBitToOne(0, y, Constants.CW_LEFT_BOUND);
setBitToOne(width-1, y, Constants.CW_RIGHT_BOUND);
}
}
public boolean areaOverlapsWithRoom(int rx, int ry, int rxl, int ryl) {
int x, y;
for (x = rx-1; x <= rxl+1; x++)
{
for (y = ry-1; y <= ryl+1; y++)
{
if (isInRoom(x, y))
return true ;
}
}
return false ;
}
private void deleteBound(int x, int y, int dx, int dy) {
setBoundToZero(x, y, dx, dy);
setBoundToZero(x+dx, y+dy, -dx, -dy) ;
}
public void addBoundWall(int x, int y, int dx, int dy) {
setBoundAndWallToOne(x, y, dx, dy);
setBoundAndWallToOne(x+dx, y+dy, -dx, -dy);
}
public void deleteWall(int x, int y, int dx, int dy) {
setWallToZero(x, y, dx, dy);
setWallToZero(x+dx, y+dy, -dx, -dy);
if (deepdebugWall)
logWall( x, y, dx, dy);
public void markAreaAsRoom(int rw, int rh, int rx, int ry, int rxl, int ryl) {
int x;
int y;
for (x = rx; x <= rxl; x++)
for (y = ry; y <= ryl; y++) {
setAllToZero(x, y);
setInRoomToOne(x, y);
}
for (x = rx; x <= rxl; x++) {
addBoundWall(x, ry, 0, -1);
addBoundWall(x, ryl, 0, 1);
}
for (y = ry; y <= ryl; y++) {
addBoundWall(rx, y, -1, 0);
addBoundWall(rxl, y, 1, 0);
}
int wallct = (rw+rh)*2;
SingleRandom random = SingleRandom.getRandom() ;
for (int ct = 0; ct != 5; ct++) {
int door = random.nextIntWithinInterval(0, wallct-1);
int dx, dy;
if (door < rw*2) {
y = (door < rw) ? 0 : rh-1;
dy = (door < rw) ? -1 : 1;
x = door % rw;
dx = 0;
} else {
door -= rw*2;
x = (door < rh) ? 0 : rw-1;
dx = (door < rh) ? -1 : 1;
y = door % rh;
dy = 0;
}
deleteBound(x+rx, y+ry, dx, dy);
}
}
public boolean hasMaskedBitsTrue(int x, int y, int bitmask) {
return (cells[x][y] & bitmask) != 0;
}
public boolean isInRoom(int x, int y) {
return hasMaskedBitsTrue(x, y, Constants.CW_IN_ROOM);
}
private boolean isFirstVisit(int x, int y) {
return hasMaskedBitsTrue(x, y, Constants.CW_VISITED);
}
public boolean hasWallOnRight(int x, int y) {
return hasMaskedBitsTrue(x, y, Constants.CW_RIGHT);
}
public boolean hasWallOnLeft(int x, int y) {
return hasMaskedBitsTrue(x, y, Constants.CW_LEFT);
}
public boolean hasWallOnTop(int x, int y) {
return hasMaskedBitsTrue(x, y, Constants.CW_TOP);
}
public boolean hasWallOnBottom(int x, int y) {
return hasMaskedBitsTrue(x, y, Constants.CW_BOT);
}
public boolean hasNoWallOnBottom(int x, int y) {
return !hasMaskedBitsTrue(x, y, Constants.CW_BOT);
}
public boolean hasNoWallOnTop(int x, int y) {
return !hasMaskedBitsTrue(x, y, Constants.CW_TOP);
}
public boolean hasNoWallOnLeft(int x, int y) {
return !hasMaskedBitsTrue(x, y, Constants.CW_LEFT);
}
public boolean hasNoWallOnRight(int x, int y) {
return !hasMaskedBitsTrue(x, y, Constants.CW_RIGHT);
}
public boolean hasMaskedBitsFalse(int x, int y, int bitmask) {
return (cells[x][y] & bitmask) == 0;
}
public boolean hasMaskedBitsGTZero(int x, int y, int bitmask) {
return (cells[x][y] & bitmask) > 0;
}
private void dbg(String str) {
System.out.println("Cells: "+str);
}
public String toString() {
String s = "" ;
for (int i = 0 ; i < width ; i++)
{
for (int j = 0 ; j < height ; j++)
s += " i:" + i + " j:" + j + "=" + cells[i][j] ;
s += "\n" ;
}
return s ;
}
static boolean deepdebugWall = false;
static final String deepedebugWallFileName = "logDeletedWalls.txt" ;
StringBuffer traceWall = (deepdebugWall) ? new StringBuffer("x y dx dy\n") : null ;
private void logWall(int x, int y, int dx, int dy) {
if (null != traceWall)
{
traceWall.append(x + " " + y + " " + dx + " " + dy + "\n");
}
}
public void saveLogFile( String filename )
{
try {
BufferedWriter out = new BufferedWriter(new FileWriter(filename));
out.write(traceWall.toString());
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//MazeBuilder.java
package falstad;
public class MazeBuilder implements Runnable {
protected int width, height ;
Maze maze;
private int rooms;
int expectedPartiters;
protected int startx, starty ;
protected Cells cells;
protected Distance dists ;
protected SingleRandom random ;
Thread buildThread;
public MazeBuilder(){
random = SingleRandom.getRandom();
}
public MazeBuilder(boolean deterministic){
if (true == deterministic)
{
System.out.println("Project 2: functionality to make maze generation deterministic not implemented yet! Fix this!");
}
random = SingleRandom.getRandom();
}
static int getSign(int num) {
return (num < 0) ? -1 : (num > 0) ? 1 : 0;
}
protected void generate() {
generatePathways();
final int[] remote = dists.computeDistances(cells) ;
final int[] pos = dists.getStartPosition();
startx = pos[0] ;
starty = pos[1] ;
setExitPosition(remote[0], remote[1]);
}
protected void generatePathways() {
int[][] origdirs = new int[width][height] ;
int x = random.nextIntWithinInterval(0, width-1) ;
int y = 0;
final int firstx = x ;
final int firsty = y ;
int dir = 0;
int origdir = dir;
cells.setVisitedFlagToZero(x, y);
while (true) {
int dx = Constants.DIRS_X[dir];
int dy = Constants.DIRS_Y[dir];
if (!cells.canGo(x, y, dx, dy)) {
dir = (dir+1) & 3;
if (origdir == dir) {
if (x == firstx && y == firsty)
break;
int odr = origdirs[x][y];
dx = Constants.DIRS_X[odr];
dy = Constants.DIRS_Y[odr];
x -= dx;
y -= dy;
origdir = dir = random.nextIntWithinInterval(0, 3);
}
} else {
cells.deleteWall(x, y, dx, dy);
x += dx;
y += dy;
cells.setVisitedFlagToZero(x, y);
origdirs[x][y] = dir;
origdir = dir = random.nextIntWithinInterval(0, 3);
}
}
}
protected void setExitPosition(int remotex, int remotey) {
int bit = 0;
if (remotex == 0)
bit = Constants.CW_LEFT;
else if (remotex == width-1)
bit = Constants.CW_RIGHT;
else if (remotey == 0)
bit = Constants.CW_TOP;
else if (remotey == height-1)
bit = Constants.CW_BOT;
else
dbg("Generate 1");
cells.setBitToZero(remotex, remotey, bit);
//System.out.println("exit position set to zero: " + remotex + " " + remotey + " " + bit + ":" + cells.hasMaskedBitsFalse(remotex, remotey, bit)
// + ", Corner case: " + ((0 == remotex && 0 == remotey) || (0 == remotex && height-1 == remotey) || (width-1 == remotex && 0 == remotey) || (width-1 == remotex && height-1 == remotey)));
}
static final int MIN_ROOM_DIMENSION = 3 ;
static final int MAX_ROOM_DIMENSION = 8 ;
private boolean placeRoom() {
final int rw = random.nextIntWithinInterval(MIN_ROOM_DIMENSION, MAX_ROOM_DIMENSION);
if (rw >= width-4)
return false;
final int rh = random.nextIntWithinInterval(MIN_ROOM_DIMENSION, MAX_ROOM_DIMENSION);
if (rh >= height-4)
return false;
final int rx = random.nextIntWithinInterval(1, width-rw-1);
final int ry = random.nextIntWithinInterval(1, height-rh-1);
final int rxl = rx+rw-1;
final int ryl = ry+rh-1;
if (cells.areaOverlapsWithRoom(rx, ry, rxl, ryl))
return false ;
cells.markAreaAsRoom(rw, rh, rx, ry, rxl, ryl);
return true;
}
static void dbg(String str) {
System.out.println("MazeBuilder: "+str);
}
public void build(Maze mz, int w, int h, int roomct, int pc) {
init(mz, w, h, roomct, pc);
buildThread = new Thread(this);
buildThread.start();
}
private void init(Maze mz, int w, int h, int roomct, int pc) {
maze = mz;
width = w;
height = h;
rooms = roomct;
expectedPartiters = pc;
cells = new Cells(w,h) ;
dists = new Distance(w,h) ;
//colchange = random.nextIntWithinInterval(0, 255);
}
static final long SLEEP_INTERVAL = 100 ;
try {
cells.initialize();
// rooms into maze
generateRooms();
Thread.sleep(SLEEP_INTERVAL) ;
// pathways into the maze,
generate();
Thread.sleep(SLEEP_INTERVAL) ;
final int colchange = random.nextIntWithinInterval(0, 255);
final BSPBuilder b = new BSPBuilder(maze, dists, cells, width, height, colchange, expectedPartiters) ;
BSPNode root = b.generateBSPNodes();
Thread.sleep(SLEEP_INTERVAL) ;
// dbg("partiters = "+partiters);
maze.newMaze(root, cells, dists, startx, starty);
}
catch (InterruptedException ex) {
// dbg("Catching signal to stop") ;
}
}
static final int MAX_TRIES = 250 ;
private int generateRooms() {
int tries = 0 ;
int result = 0 ;
while (tries < MAX_TRIES && result <= rooms) {
if (placeRoom())
result++ ;
else
tries++ ;
}
return result ;
}
public void interrupt() {
buildThread.interrupt() ;
}
}
显然,我并不是要求有人为我写一个测试——你们中的任何人都不会这样做,因为这显然是一项家庭作业——但我希望得到一些帮助。因此,如果有人能提示我了解我所缺少的明显的技巧,那就太好了!