# Triangle Peg Solitaire(孔明棋)

GameBoard.java:此类是为了生成三角形状的棋盘
package com.game.board;

import java.util.List;
import java.util.ArrayList;

public class GameBoard {

// board consists of pegs and holes in a 5 x 5, but only uses triangular pyramid
boolean[][] pins = new boolean[5][5];

public GameBoard(int row, int col) {
for (int i = 0; i < 5; ++i)
for (int j = 0; j <= i; ++j)
pins[i][j] = true;
pins[row][col] = false;
}

public GameBoard(int board) {
for (int i = 4; i >= 0; --i)
for (int j = i; j >= 0; --j) {
if ((board & 1 ) == 1)
pins[i][j] = true;
else
pins[i][j] = false;
board /= 2;
}

}

// copy constructor
public GameBoard(GameBoard that) {
for (int i = 0; i < 5; ++i)
for (int j = 0; j <= i; ++j)
pins[i][j] = that.pins[i][j];
}

public List<GameBoard> possibleBoards() {
List<GameBoard> boards = new ArrayList<GameBoard>();

for (int i = 0; i < 5; ++i)
for (int j = 0; j <= i; ++j) {
Position start = new Position(i,j);
List<Move> possibleMoves = Moves.getMoves(start);
for (Move move : possibleMoves) {
if (validMove(move))
}
}

return boards;
}

public boolean validMove(Move move) {
if (!pins[move.getStart().getRow()][move.getStart().getCol()])	// empty start
return false;
if (!pins[move.getJump().getRow()][move.getJump().getCol()])	// empty jump over
return false;
if (pins[move.getEnd().getRow()][move.getEnd().getCol()])		// filled in end
return false;

return true;
}

public GameBoard jump(Move move) {
GameBoard gb = new GameBoard(this);

gb.pins[move.getStart().getRow()][move.getStart().getCol()] = false;
gb.pins[move.getJump().getRow()][move.getJump().getCol()] = false;
gb.pins[move.getEnd().getRow()][move.getEnd().getCol()] = true;

return gb;
}

public boolean finalBoard() {
int remainingPins = 0;

for (int i = 0; i < 5; ++i)
for (int j = 0; j <= i; ++j)
if (pins[i][j]) {
remainingPins++;
if (remainingPins > 1)	// optimize, early out, more than 1 pin is not final board
return false;
}

return remainingPins == 1;
}

public int toInt() {
int ret = 0;
for (int i = 0; i < 5; ++i)
for (int j = 0; j <= i; ++j) {
ret *= 2;
if (pins[i][j]) {
ret |= 1;
}
}

return ret;
}

public String toString() {
StringBuffer sb = new StringBuffer();

for (int i = 0; i < 5; ++i) {
for (int s = 4-i; s > 0; --s)
sb.append(" ");
for (int j = 0; j <= i; ++j) {
sb.append(pins[i][j] ? 'X' : 'O').append(" ");
}
sb.append("\n");
}
//		sb.append(toInt()+ "\n");

return sb.toString();
}
}

Move.java：移动的坐标指向类
package com.game.board;

public class Move {
private Position start;
private Position jump;
private Position end;

public Move(Position start, Position jump, Position end) {
this.start = start;
this.jump = jump;
this.end = end;
}

public Position getStart() { return start; }
public Position getJump() { return jump; }
public Position getEnd() { return end; }

public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("{"+start);
sb.append(","+jump);
sb.append(","+end+ "}");

return sb.toString();
}
}

Moves.java：将所有起始点可以移动的方向全部列举并放入map集合中
package com.game.board;

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

public class Moves {
private static Map<Position,List<Move>> validMoves = new HashMap<Position,List<Move>>();

static {
/*
*          0,0
*       1,0  1,1
*     2,0  2,1  2,2
*   3,0  3,1  3,2  3,3
* 4,0  4,1  4,2  4,3  4,4
*
*/
Position start;

start = new Position(0,0);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(1,0), new Position(2,0)));
validMoves.get(start).add(new Move(start, new Position(1,1), new Position(2,2)));

start = new Position(1,0);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(2,0), new Position(3,0)));
validMoves.get(start).add(new Move(start, new Position(2,1), new Position(3,2)));

start = new Position(1,1);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(2,1), new Position(3,1)));
validMoves.get(start).add(new Move(start, new Position(2,2), new Position(3,3)));
/*
*          0,0
*       1,0  1,1
*     2,0  2,1  2,2
*   3,0  3,1  3,2  3,3
* 4,0  4,1  4,2  4,3  4,4
*
*/
start = new Position(2,0);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(1,0), new Position(0,0)));
validMoves.get(start).add(new Move(start, new Position(2,1), new Position(2,2)));
validMoves.get(start).add(new Move(start, new Position(3,0), new Position(4,0)));
validMoves.get(start).add(new Move(start, new Position(3,1), new Position(4,2)));

start = new Position(2,1);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(3,1), new Position(4,1)));
validMoves.get(start).add(new Move(start, new Position(3,2), new Position(4,3)));

start = new Position(2,2);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(1,1), new Position(0,0)));
validMoves.get(start).add(new Move(start, new Position(2,1), new Position(2,0)));
validMoves.get(start).add(new Move(start, new Position(3,2), new Position(4,2)));
validMoves.get(start).add(new Move(start, new Position(3,3), new Position(4,4)));
/*
*          0,0
*       1,0  1,1
*     2,0  2,1  2,2
*   3,0  3,1  3,2  3,3
* 4,0  4,1  4,2  4,3  4,4
*
*/
start = new Position(3,0);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(2,0), new Position(1,0)));
validMoves.get(start).add(new Move(start, new Position(3,1), new Position(3,2)));

start = new Position(3,1);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(2,1), new Position(1,1)));
validMoves.get(start).add(new Move(start, new Position(3,2), new Position(3,3)));

start = new Position(3,2);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(2,1), new Position(1,0)));
validMoves.get(start).add(new Move(start, new Position(3,1), new Position(3,0)));

start = new Position(3,3);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(2,2), new Position(1,1)));
validMoves.get(start).add(new Move(start, new Position(3,2), new Position(3,1)));
/*
*          0,0
*       1,0  1,1
*     2,0  2,1  2,2
*   3,0  3,1  3,2  3,3
* 4,0  4,1  4,2  4,3  4,4
*
*/
start = new Position(4,0);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(3,0), new Position(2,0)));
validMoves.get(start).add(new Move(start, new Position(4,1), new Position(4,2)));

start = new Position(4,1);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(3,1), new Position(2,1)));
validMoves.get(start).add(new Move(start, new Position(4,2), new Position(4,3)));

start = new Position(4,2);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(3,1), new Position(2,0)));
validMoves.get(start).add(new Move(start, new Position(3,2), new Position(2,2)));
validMoves.get(start).add(new Move(start, new Position(4,1), new Position(4,0)));
validMoves.get(start).add(new Move(start, new Position(4,3), new Position(4,4)));

start = new Position(4,3);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(3,2), new Position(2,1)));
validMoves.get(start).add(new Move(start, new Position(4,2), new Position(4,1)));

start = new Position(4,4);
validMoves.put(start, new ArrayList<Move>());
validMoves.get(start).add(new Move(start, new Position(3,3), new Position(2,2)));
validMoves.get(start).add(new Move(start, new Position(4,3), new Position(4,2)));
}

public static List<Move> getMoves(Position position) {
if (!validMoves.containsKey(position))
throw new RuntimeException("Invalid position: " + position);

return validMoves.get(position);
}

public String toString() {
return validMoves.toString();
}
}

position.java：坐标类
package com.game.board;

public class Position {
int row;
int col;

public Position(int row, int col) {
this.row = row;
this.col = col;
}

public int getRow() { return row; }
public int getCol() { return col; }

public String toString() {
return "[" + row + "," + col + "]";
}

public int hashCode() {
int result = 17;

result = 37*result + row;
result = 37*result + col;

return result;
}

public boolean equals(Object other) {
if (!(other instanceof Position))
return false;
Position that = (Position) other;

if (this.row != that.row)
return false;

return this.col == that.col;
}
}

GameTree.java：以树的形式保存遍历的所有路径
package com.game.play;

import java.util.List;
import java.util.ArrayList;

import com.game.board.GameBoard;

public class GameTree {
GameTree level;

GameBoard gb;
List<GameTree> children = new ArrayList<GameTree>();

public GameTree(GameBoard gb) {
this.gb = gb;
}

}

public GameBoard getGameBoard() { return gb; }

public boolean hasChildren() {
return children.size() > 0;
}

public GameTree getFirstChild() {
return children.get(0);
}

public void removeFirstChild() {
children.remove(0);
}

public int numChildren() {
return children.size();
}
}

Play.java：通过DFS搜索所有可能的路径
package com.game.play;

import java.io.IOException;
import java.util.List;

import com.game.board.GameBoard;

public class Play {

GameBoard startBoard;

public Play(String[] args) throws IOException {
if (args.length == 0) {
int n1,n2;
System.out.println("Name: samudra ");
System.out.println("......................................");
System.out.println("Enter the position of the hole:");
init(n1,n2);
}
else
init(Integer.parseInt( args[0]),Integer.parseInt( args[1]));
}

private void init(int row, int col) {
startBoard = new GameBoard(row, col);
}

//DFS function
public void DFS() {
GameTree root = new GameTree(startBoard);

for (GameBoard nextBoard : startBoard.possibleBoards()) {
GameTree nextNode = new GameTree(nextBoard);
if (play(nextBoard, nextNode))
}

printWinningGame(root);

}

// to iterate is human, to recurse divine
// print game board at each node on the way down, removing the printed child on the way up
private void printWinningGame(GameTree parent) {
System.out.println(parent.getGameBoard());

if (parent.numChildren() > 0) {
GameTree nextNode = parent.getFirstChild();
printWinningGame(nextNode);				// recurse
if (nextNode.numChildren() == 0)
parent.removeFirstChild();
} else {
System.out.println("===============================================");
}
}

// chase all possible boards
private boolean play(GameBoard gb, GameTree parent) {

if (gb.finalBoard())	// remember this path was a winning path
return true;

List<GameBoard> nextBoards = gb.possibleBoards();

boolean found = false;

for (GameBoard nextBoard : nextBoards) {
GameTree nextNode = new GameTree(nextBoard);
if (play(nextBoard, nextNode)) {				// recurse
found = true;
}
}

return found;
}

}

PegBord.java：主方法
package com.game.main;

import java.io.IOException;

import com.game.play.Play;

public class PegBoard {

/**
* @param args
*/
public static void main(String[] args) throws IOException {
Play play = new Play(args);
play.DFS();
}

}


Name: samudra
......................................
Enter the position of the hole:
2
1
X
X X
X O X
X X X X
X X X X X

X
X X
X X X
X O X X
X O X X X

X
X X
X X X
X X O O
X O X X X

X
O X
X O X
X X X O
X O X X X

X
O O
X O O
X X X X
X O X X X

X
X O
O O O
O X X X
X O X X X

O
O O
X O O
O X X X
X O X X X

O
O O
X O O
O X X X
X X O O X

O
O O
O O O
O O X X
X X X O X

O
O O
O O O
O O X X
X O O X X

O
O O
O O X
O O X O
X O O X O

O
O O
O O O
O O O O
X O X X O

O
O O
O O O
O O O O
X X O O O

O
O O
O O O
O O O O
O O X O O

===============================================