孔明棋,是法国跳棋独立钻石在中国的别称,也有人叫它跳弹珠,或者叫它「Pegged」。一般流传的玩法是先取去中央的那个棋子,便可以展开游戏。游戏时,是将棋子跳过邻近的棋子,到达一个旁边空着的位置,被跳过的棋子则从棋盘上取开;跳的路径可以前,后,左,右,但不可对角跳,直到剩下最后的一颗棋子,游戏便结束了。这是一种流传很广的益智游戏,也有很多种变形的棋盘摆法,而本文要讲的是在三角形棋盘上的玩法,它是自17世纪末就在欧洲开始被人们玩起来了。当各大餐厅为每一个餐桌上等待食物的顾客都放置一个棋盘以供他们娱乐的时候,这类益智游戏变得越来越流行。
本文实际上通过一段简单的回溯算法DFS去搜索找出一个解决给定棋盘上的出发点的解决方案。
背景
这个方案的主要目的是陈述一个递归函数的概念,本质上就是递归问题和回溯。它还提供了一个面向对象的思维,在一个可重用的类里来保存所有回溯逻辑形式。解决这个难题,需要使用被称为回溯的技术来搜索所有可能的移动路径,这是很容易使用递归实现的。
基本的步骤
三角之谜是15孔组织成一个三角形的集合,在其中放置钉,留下至少一个空孔。通常,该益智游戏开始只有一个空孔,如在结构(a)所示,其中实心圆圈代表钉,而空的圆圈代表孔。解谜的目的是通过一系列合法的移动,每个移动造成一个钉被删除,直到最后只剩下一个钉。一个钉被删除仅仅是通过跳过它的其他钉。因此,一个合法的移动只能是由一个位于孔和其他钉的侧面的钉移动,它们排列在一行。例如,结构(b)所示的结构是由结构(a)经过一次跳跃获得,然后(c)又由(b)再次一次跳跃得到。
代码
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 pyramidboolean[][] 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;elsepins[i][j] = false;board /= 2;}}// copy constructorpublic 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))boards.add(jump(move));}}return boards;}public boolean validMove(Move move) {if (!pins[move.getStart().getRow()][move.getStart().getCol()])  // empty startreturn false;if (!pins[move.getJump().getRow()][move.getJump().getCol()]) // empty jump overreturn false;if (pins[move.getEnd().getRow()][move.getEnd().getCol()])        // filled in endreturn 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 boardreturn 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 void addChild(GameTree child) {children.add(child);}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 java.io.BufferedReader;
import java.io.InputStreamReader;import com.game.board.GameBoard;public class Play {GameBoard startBoard;public Play(String[] args) throws IOException {if (args.length == 0) {int n1,n2;BufferedReader stdin = new BufferedReader (new InputStreamReader(System.in));System.out.println("Name: samudra ");System.out.println("......................................");System.out.println("Enter the position of the hole:");n1=Integer.parseInt( stdin.readLine());n2=Integer.parseInt( stdin.readLine());init(n1,n2);}elseinit(Integer.parseInt( args[0]),Integer.parseInt( args[1]));}private void init(int row, int col) {startBoard = new GameBoard(row, col);}//DFS functionpublic void DFS() {GameTree root = new GameTree(startBoard);for (GameBoard nextBoard : startBoard.possibleBoards()) {GameTree nextNode = new GameTree(nextBoard);if (play(nextBoard, nextNode))root.addChild(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 upprivate void printWinningGame(GameTree parent) {System.out.println(parent.getGameBoard());if (parent.numChildren() > 0) {GameTree nextNode = parent.getFirstChild();           printWinningGame(nextNode);             // recurseif (nextNode.numChildren() == 0)    parent.removeFirstChild();} else {System.out.println("===============================================");}}// chase all possible boardsprivate boolean play(GameBoard gb, GameTree parent) {if (gb.finalBoard())    // remember this path was a winning pathreturn true;List<GameBoard> nextBoards = gb.possibleBoards();boolean found = false;for (GameBoard nextBoard : nextBoards) {GameTree nextNode = new GameTree(nextBoard);if (play(nextBoard, nextNode)) {                // recursefound = true;parent.addChild(nextNode);}}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 O X X 
X O X X X


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


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


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


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


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


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


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


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


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


   O O 
  O O O 
 O O O O 
X O X X 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 X O O

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

Triangle Peg Solitaire(孔明棋)相关推荐

  1. Go语言解决孔明棋的玩法(加深对for循环的认识)

    对于for循环,在 Go语言零基础入门,捕获错误.slice切片.for循环.test 中也有介绍,大家都很熟悉,一般是三部分组成:初始化,条件表达式,后置语句,如下: func main() {fo ...

  2. 通过python递归来解决孔明棋问题

    孔明棋大家应该都不陌生.(好吧可能不一定) 简单来讲,是一种规则简单,但是过程并不见得容易,可以一个人玩的棋. 他有还一个有趣的名字:法国跳棋独立钻石. 我上手的是vx某小程序.玩法比最经典的32颗棋 ...

  3. 我用VB做的孔明棋游戏

    我用VB做的孔明棋游戏 附源码 http://download1.csdn.net/down3/20070530/30200905916.rar

  4. 类似孔明棋,寻找棋局中到达目标点的最短路径(深搜和广搜)

    主题内容: 有个游戏玩法很类似孔明棋. 其游戏的原始规则如下: 原始棋盘为这样: 假设0为空格 1为棋子 0000000 0000000 0000000 0000000 1111111 1111111 ...

  5. 类孔明棋 c语言实验,深度优先搜索中的重叠子结果的优化问题

    前几天帮朋友写一个类孔明棋的算法, 上题       5*5的棋盘   .表示没棋子 o表示有棋子 如:  ooooo ooooo oo.oo  ooooo ooooo 走法和孔明的走法一样 开始直接 ...

  6. Python小游戏——孔明棋

    Python小游戏--孔明棋 规则简介 孔明棋又叫法国独立钻石,是一种单人棋,下法规则简单,棋子只能跳过相邻的棋子到空位上,并且把被跳过的棋子吃掉.棋子可以沿格线横.纵方向跳,但是不能斜跳,当棋盘内所 ...

  7. URAL 1051 跳跳棋(孔明棋)(加强版) 数学找规律(较弱哦)

    跳跳棋(加强版) 问题描述 hty想到最近出过一道1*N的跳跳棋游戏,于是他想是否2维也能有很好的方法呢?于是有了下面这道题,题目如下:在一个无限大的棋盘的格子上有一些棋子,这些棋子构成一个M*N的矩 ...

  8. [构造 找规律 孔明棋] Ural 1051 Simple Game on a Grid

    论文:俞鑫--棋盘中的棋盘--浅谈棋盘的分割思想 证明篇幅太长就不弄过来了 #include<cstdio>int main(){int n,m,ans;freopen("t.i ...

  9. 独立钻石棋游戏设计制作(C语言)

    独立钻石棋游戏设计制作(C语言) 我是编程爱好者.早期使用windows时就被扫雷游戏,钻石棋游戏,红心大战游戏迷住.相信大家也都玩过吧.在学习VB6编程时就编制过钻石棋和红心大战那样的纸牌游戏.都是 ...

最新文章

  1. 电信的 DNS 服务器地址
  2. Go gin静态文件的使用、自定义模板渲染器
  3. springboot @RequestBody 接收字符串
  4. dojo/request模块整体架构解析
  5. linux安全模式改文件,嵌入式Linux的安全模式设计 - 嵌入式操作系统 - 电子发烧友网...
  6. 警告—系统—srv—2013—无
  7. vs2017 js cordova + dotnet core 开发app
  8. python 测试开发请关注这个新框架
  9. dx绘制2d图像_【3D建模】聊聊2D动画软件
  10. 华为鸿蒙巴龙麒麟,华为5G新机强势曝光:麒麟985+巴龙5000+鸿蒙系统,颜值性能炸裂...
  11. spine怎么取消版本升级_设置 - Spine用户指南
  12. party_bid_core总结
  13. Google加速移动页面(AMP)简介
  14. HTML 媒体、表单和音视频笔记
  15. 新手向:如何用python打开网址
  16. sap的清账是什么意思_SAP清账的高手剖析
  17. 微信关注即可使用 Wi-Fi,取消关注即断网的路由器实现的流程原理以及步骤
  18. Bit(位)与Byte(字节)的区别
  19. 基石为勤能补拙的迷宫之旅——第一天(计算机硬件和操作系统)
  20. 最近几年的国内开源软件侵权事件

热门文章

  1. Mac下使用Xquartz连接CentOS的Xdm服务器
  2. SDCC 2016系列多站回顾:精彩集锦 百家争鸣(附PPT下载)
  3. oracle默认表达式uuid,Oracle中生成uuid的方法
  4. 鼠标精灵对码软件_Photo Frame 图片编辑软件(相框精灵)
  5. 停车场收费管理系统/停车场管理系统的设计与实现
  6. 12.1-12.8 计算机网络课堂笔记
  7. 在软件开发中实施人工智能和敏捷管理的9种方法
  8. 如何快速制作彩色二维码图片?
  9. 【Discuz】dz3.2论坛搬家心得
  10. MySQL 案例实战--MySQL 数据库 之 冷备份