题目要求:
假设已有15*15的五子棋棋盘(0-14),黑白子对下,现要求能够实现落子接口:
1)落子成功时,如果当前颜色的棋子胜利(有5子连成一条线),黑子胜利返回2,白子胜利返回3,没有人胜利时,返回落子成功1;
2)黑白子顺序混乱、或者重复落子等异常返回-1;
分析:
1、需要实现保存所下所有棋子的数据;
2、在1前提下,能够判定所下棋子是否有5个子在一条直线上(2条直线、2条对角线),即实现胜利的判定算法;
3、需要保存黑子、白子落子的顺序,处理落子失败等异常;
感觉5子的直线判定算法应该是其中比较麻烦的,我的初步想法是黑子或者白子落子后,遍历其所有下过的棋子,分别遍历2条直线和2条对角线上的棋子是否有5个;
如判定水平线上是否有5子成线:
1)取一点A,判断其右边的点是否在下过的棋子中,是就把水平线上棋子的统计数量加1,继续向右遍历;
2)如果棋子的统计数量加等于5则返回胜利;否则如果右边已经没有下一点在所下棋子中,同时统计数量小于5,则从A点向左遍历,统计继续累加,等于5时返回胜利;
其它三个方向类似;
仔细想想发现没必要遍历每个点,只对最后落的子B进行如上遍历即可。因为如果存在5个点,不与B相邻且是相邻节点,且在一条直线上,那么在B棋子落下之前就已经胜利了;
算法就说这么多;
说说面向对象设计思路:
1、定义1个棋盘类,保存棋盘大小,可对棋子做是否越界判定;
2、定义棋手类,能够保存所下棋子的位置,并在落子后,判定最后的落子是否能够与其它位置的棋子形成5子一线;
3、定义棋子枚举,规定是黑子还是白子,被棋手类引用;
4、定义位置类,点坐标;
5、定义游戏类:拥有一个棋盘和2个棋手,定义了落子参数的入口。
这个问题比较简单:
直接上代码
/**
*
* 类名称:ChessConstants 类描述: 创建人:dobuy
*
*/
public interface ChessConstants
{
/**
* 落子成功
*/
int SUCCESS = 1;
/**
* 落子失败:顺序不对、位置非法、指定棋手非法
*/
int FAIL = -1;
/**
* 黑子赢
*/
int BLACK_WIN = 2;
/**
* 白子赢
*/
int WHITE_WIN = 3;
/**
* 黑子
*/
int BLACK = 0;
/**
* 白子
*/
int WHITE = 1;
/**
* 错误的棋手编号
*/
int ERROR = -1;
/**
* 赢时一条线上的棋子数量
*/
int WIN_OF_LINE_CHESSES = 5;
int DEFAULT_TURN = -2;
}
/**
* 棋盘(棋子的地图)
*
* @author dobuy
* @time 2013-5-12
*/
public class Chessboard
{
/**
* 棋盘的最小边界点(原点)
*/
private Position origonPosition;
/**
* 棋盘的最大边界点
*/
private Position edgePosition;
public Chessboard()
{
this.origonPosition = new Position(0, 0);
this.edgePosition = new Position(14, 14);
}
/**
* 当前位置在棋盘中是否越界
*
* @return
*/
public boolean isOverEdge(Position currentPosition)
{
if (currentPosition.getX() < getOrigonPosition().getX()
|| currentPosition.getY() < getOrigonPosition().getY()
|| currentPosition.getX() > getEdgePosition().getX()
|| currentPosition.getY() > getEdgePosition().getY())
{
return true;
}
return false;
}
private Position getEdgePosition()
{
return edgePosition;
}
private Position getOrigonPosition()
{
return origonPosition;
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
* 类名称:ChessPlayer 类描述: 棋手 创建人:dobuy
*
*/
public class ChessPlayer
{
/**
* 棋子的类型:黑棋/白棋
*/
private ChessmanType chessmanType;
/**
* 已经落的棋子位置
*/
private List<Position> chessmans;
/**
* 棋子连成一条线(相邻2点)的偏移量, 只列了正方向,如:向下、向右、向右下、右上, 另4个方向通过Position的reversal()方法来表达
*/
private List<Position> offsets = Arrays.asList(new Position(0, 1),
new Position(1, 0), new Position(1, 1), new Position(1, -1));
public ChessPlayer(ChessmanType type)
{
this.chessmanType = type;
this.chessmans = new ArrayList<Position>();
}
public ChessmanType getChessmanType()
{
return chessmanType;
}
public void addChessman(Position chessman)
{
getChessmans().add(chessman);
}
public boolean contains(Position chessman)
{
return getChessmans().contains(chessman);
}
public void clear()
{
getChessmans().clear();
}
/**
* 判断是否有五子连成线,只需判断以最后下的棋子为中心的2条对接线和2条直线即可
*
*/
public boolean isWin()
{
if (getChessmans().isEmpty())
{
return false;
}
Position currentPosition = getLastChessman();
int lineOfChesses = 0;
Position nextPosition = null;
boolean hasReversal = false;
// 依次遍历2条直线上和2条对角线上的点,遍历每条直线时,当正向找不到时,开始反向找
for (Position offset : getOffsets())
{
lineOfChesses = 1;
nextPosition = currentPosition.offset(offset);
// 是否反向找过了
hasReversal = false;
while ((contains(nextPosition) || !hasReversal))
{
// 不包含这个点时,说明正向直线上已经找不到下一个点了,开始反向找,下一个指向正向的第一个点
if (!contains(nextPosition))
{
hasReversal = true;
nextPosition = currentPosition;
offset = offset.reversal();
}
else
{
lineOfChesses++;
}
if (ChessConstants.WIN_OF_LINE_CHESSES == lineOfChesses)
{
return true;
}
nextPosition = nextPosition.offset(offset);
}
}
return false;
}
/**
* 获取最近下的棋子位置
*
*/
private Position getLastChessman()
{
int size = getChessmans().size();
return getChessmans().get(size - 1);
}
private List<Position> getChessmans()
{
return chessmans;
}
private List<Position> getOffsets()
{
return offsets;
}
}
public enum ChessmanType
{
BLACK(ChessConstants.BLACK), WHITE(ChessConstants.WHITE), ERROR(
ChessConstants.ERROR);
private int type;
private ChessmanType(int type)
{
this.type = type;
}
public int getChessmanType()
{
return this.type;
}
/**
* 查找指定的棋手,找不到返回错误信息
*
*/
public static ChessmanType getChessmanTypeByNo(int playerNo)
{
ChessmanType[] types = ChessmanType.values();
for (ChessmanType chessmanType : types)
{
if (chessmanType.getChessmanType() == playerNo)
{
return chessmanType;
}
}
return ERROR;
}
}
/**
* 坐标位置对象
*
* @author dobuy
* @time 2013-5-12
*/
public class Position
{
private int x;
private int y;
public Position(int x, int y)
{
super();
this.x = x;
this.y = y;
}
/**
* 获取偏移后的位置
*
* @param offset 偏移量
* @return
*/
public Position offset(Position offset)
{
return new Position(getX() + offset.getX(), getY() + offset.getY());
}
/**
* 偏移量的X,Y坐标取反
*
* @return
*/
public Position reversal()
{
return new Position(-getX(), -getY());
}
public int getX()
{
return x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int y)
{
this.y = y;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Position other = (Position) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
@Override
public String toString()
{
return "Position [x=" + x + ", y=" + y + "]";
}
}
/**
*
* 类名称:ChessGame 类描述: 创建人:dobuy
*
*/
public class ChessGame
{
private Chessboard chessboard;
private ChessPlayer player1;
private ChessPlayer player2;
private int turnNo = ChessConstants.DEFAULT_TURN;
public ChessGame()
{
chessboard = new Chessboard();
player1 = new ChessPlayer(ChessmanType.BLACK);
player2 = new ChessPlayer(ChessmanType.WHITE);
}
/**
* 启动游戏: 每次落一子,如果棋子位置在棋盘上,且落子顺序正常(黑子白子交替下), 则添加棋子到棋盘上,如果没人赢,返回落子成功;
* 如果当前黑子/白子赢了,则返回黑子/白子赢的编号(非棋手的编号)
*
* @param playerNo:玩家序号:黑子棋手/白子棋手
* @param position 落子位置
*
*/
public int startGame(int playerNo, int[] position)
{
ChessmanType chessmanType = ChessmanType.getChessmanTypeByNo(playerNo);
if (ChessmanType.ERROR == chessmanType)
{
return ChessConstants.FAIL;
}
// 位置非法:参数非法、重复落子、棋子位置越界
if (isIllegalPosition(position))
{
return ChessConstants.FAIL;
}
if (getTurnNo() == ChessConstants.DEFAULT_TURN)
{
setTurnNo(chessmanType.getChessmanType());
}
// 不是第一次落子时,判断上一次落子的下一个选手的序号是否和输入相同
else
{
if ((getTurnNo() + 1) % 2 != playerNo)
{
return ChessConstants.FAIL;
}
setTurnNo(playerNo);
}
ChessPlayer currentPlayer = getCurrentChessPlayer(chessmanType);
currentPlayer.addChessman(new Position(position[0], position[1]));
if (currentPlayer.isWin())
{
reset();
return playerNo == ChessConstants.BLACK ? ChessConstants.BLACK_WIN
: ChessConstants.WHITE_WIN;
}
return ChessConstants.SUCCESS;
}
private void reset()
{
getPlayer1().clear();
getPlayer2().clear();
setTurnNo(ChessConstants.DEFAULT_TURN);
}
private ChessPlayer getCurrentChessPlayer(ChessmanType chessmanType)
{
if (getPlayer1().getChessmanType() == chessmanType)
{
return getPlayer1();
}
else
{
return getPlayer2();
}
}
/**
* 判断当前棋子是否合法:包括参数不合法、超越棋盘边界、重复落子
*
*/
private boolean isIllegalPosition(int[] position)
{
if (null == position || position.length != 2)
{
return true;
}
Position pos = new Position(position[0], position[1]);
return isIllegalPosition(pos);
}
private boolean isIllegalPosition(Position position)
{
// 越界
if (getChessboard().isOverEdge(position))
{
return true;
}
// 重复落子
if (getPlayer1().contains(position) || getPlayer2().contains(position))
{
return true;
}
return false;
}
private Chessboard getChessboard()
{
return chessboard;
}
private ChessPlayer getPlayer1()
{
return player1;
}
private ChessPlayer getPlayer2()
{
return player2;
}
private int getTurnNo()
{
return turnNo;
}
private void setTurnNo(int turnNo)
{
this.turnNo = turnNo;
}
}
测试类:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
*
* 类名称:ChessGameTest 类描述: 创建人:dobuy
*
*/
public class ChessGameTest
{
private static ChessGame game = new ChessGame();
/**
* 落子成功
*/
int SUCCESS = 1;
/**
* 落子失败:顺序不对、位置非法、指定棋手非法
*/
int FAIL = -1;
/**
* 黑子赢
*/
int BLACK_WIN = 2;
/**
* 白子赢
*/
int WHITE_WIN = 3;
/**
* 黑子
*/
int BLACK = 0;
/**
* 白子
*/
int WHITE = 1;
/**
* CASE1:正常流程
*/
@Test
public void testStartGame1()
{
// 2条直线
assertEquals(game.startGame(BLACK, new int[] { 4, 4 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 5, 3 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 5, 4 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 7, 6 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 6, 4 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 9, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 7, 4 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 2, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 8, 4 }), BLACK_WIN);
assertEquals(game.startGame(BLACK, new int[] { 4, 4 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 5, 3 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 4, 5 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 7, 6 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 4, 6 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 9, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 4, 7 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 2, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 4, 8 }), BLACK_WIN);
// 2条对角线
assertEquals(game.startGame(BLACK, new int[] { 6, 6 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 9, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 7, 7 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 2, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 4, 4 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 5, 3 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 5, 5 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 7, 6 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 8, 8 }), BLACK_WIN);
assertEquals(game.startGame(BLACK, new int[] { 4, 6 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 9, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 6, 4 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 2, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 7, 3 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 5, 3 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 5, 5 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 8, 9 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 8, 2 }), BLACK_WIN);
}
/**
* CASE2:参数非法
*/
@Test
public void testStartGame2()
{
assertEquals(game.startGame(BLACK, new int[] { 4 }), FAIL);
assertEquals(game.startGame(BLACK, new int[] { 4, -1 }), FAIL);
assertEquals(game.startGame(BLACK, new int[] { -1, 4 }), FAIL);
assertEquals(game.startGame(BLACK, new int[] { 7, 15 }), FAIL);
}
/**
* CASE3:重复落子、顺序不对
*/
@Test
public void testStartGame3()
{
assertEquals(game.startGame(BLACK, new int[] { 4, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 10, 4 }), FAIL);
assertEquals(game.startGame(WHITE, new int[] { 4, 4 }), FAIL);
assertEquals(game.startGame(WHITE, new int[] { 5, 3 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 4, 5 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 7, 6 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 4, 6 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 9, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 4, 7 }), SUCCESS);
assertEquals(game.startGame(WHITE, new int[] { 2, 4 }), SUCCESS);
assertEquals(game.startGame(BLACK, new int[] { 4, 8 }), BLACK_WIN);
}
}