任务三 象棋中马遍历棋盘
【问题描述】
在N*N棋盘上,任意一个位置放置一个棋子马,要能选择一套合适的移动路线,按象棋中“马走日”的移动规则不重复地遍历棋盘上每一个位置点。
【基本要求】
用图形化的界面显示结果。
【扩展要求】
有兴趣的同学可在棋盘上设置一个或多个憋马腿的棋子,尝试遍历过程如何完善。
设计思路:
1.考虑落下马的位置,马可能走到的位置,即最初定义两个数组分别储存马在二维空间中可能跳跃的八个位置。
2.其次是要用到递归的方法解决马不断跳跃的问题,所以要先学会递归的基本思想。
递归思想就是多次调用自身,也就是用解决一次马跳跃的问题就意味着可以多次解决马跳跃的问题。
- 最后要考虑到回溯,马的跳跃不可能是一帆风顺的,当碰到阻碍时,马需要跳回原来的位置重新选择路线。
- 最后设计到一些界面设计问题,比如调节窗口大小、字体大小和颜色、标题等等。
【代码提示】
函数DFS从(x,y)开始,按马走日字的方法,能够走遍所有的格子时,返回True,否则返回false。
/* 马走日字,可能到达的8个点相对当前点的偏移坐标 */
static int dx[] = { 1, 2, 2, 1, -1, -2, -2, -1 };
static int dy[] = { 2, 1, -1, -2, -2, -1, 1, 2 };
/* 马走日字,8个蹩脚的点相对当前点的偏移坐标 */
static int dx_obstacle[]={0,1,1,0,0,-1,-1,0};
static int dy_obstacle[]={1,0,0,-1,-1,0,0,1};
static boolean DFS(int x, int y) {
/* 经过了所有的空位置=所有的格子数-预留的棋子数 */
if (count == boardSize * boardSize-obstacleNum)
return true;
/* 遍历从当前点走日字能够到达的8个点 */
for (int dir = 0; dir < 8; dir++) {
/*走日字到达下一个点 */
int next_x = x + dx[dir];
int next_y = y + dy[dir];
/*马走下一个点,蹩脚的位置*/
int obstacle_x=x+dx_obstacle[dir];
int obstacle_y=y+dy_obstacle[dir];
/* 超出边界 */
if (next_x < 1 || next_x > boardSize || next_y < 1
|| next_y > boardSize)
continue;
//这个位置走过或蹩马腿的位置有棋子(卒)
if (!button[next_x][next_y].getText().equals("") ||
button[obstacle_x][obstacle_y].getText().equals("卒"))
continue;
/* 访问过的点计数 */
count++;
/* 可以走一步,在下一个位置放入步数 */
button[next_x][next_y].setText(String.valueOf(count));
if (DFS(next_x, next_y)) //从下一个位置开始,继续遍历
return true;
/* 从日字的一个对角点出发不能到达所有的位置 */
count--; // 后退一步
button[next_x][next_y].setText("");
}
return false;
}
}
**代码实现----无蹩脚**-
```java
package TraversalOfHorseInChess;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @author Programming Bear
* @date 2021/06/22 15:07
**/
public class TestChess extends JFrame implements ActionListener {
/**serialVersionUID-----用来表明类的不同版本间的兼容性。
* 序列化的时候,被序列化的类要有一个唯一标记。
* 客户端和服务端必须需要同一个对象,serialVersionUID的唯一值判定其为同一个对象。
* 后面的号码是自动生成的,只要是唯一的就行,通常为1,此行语句去掉在练习的时候也没有什么影响,只不过此实例类会报一个警告。
* 将鼠标放到警告上,选择第一个解决方案,就会重新加上此行语句,后面的数字和原先的可能会不一样。
* */
private static final long serialVersionUID = -1047298397568411277L;
//马走日子的偏坐标
static int dx[] = {1, 2, 2, 1, -1, -2, -2, -1};
static int dy[] = {2, 1, -1, -2, -2, -1, 1, 2};
//棋盘的按钮
private static JButton button[][];
//棋盘大小(行数/列数)
private static int boardSize;
//落子的顺序
private static int count;
//提示信息标签
JLabel promptLabel = new JLabel();
public TestChess() {
//落子顺序
count = 1;
//定义font为微软雅黑,普通,24号
Font font = new Font("宋体", Font.PLAIN, 24);
//设置窗体标题
this.setTitle("象棋中马的遍历");
//正式面板
JPanel p1 = new JPanel();
//GridLayout为网格布局 bodrdSize*boardSize
p1.setLayout(new GridLayout(boardSize, boardSize));
//设置按钮个数
button = new JButton[boardSize + 1][boardSize + 1];
int n = 1;
for (int i = 1; i <= boardSize; i++) {
for (int j = 1; j <= boardSize; j++) {
button[i][j] = new JButton();
//动作命令,并非按钮上面的字,区别是哪个按钮
button[i][j].setActionCommand(String.valueOf(n++));
//设置字体
button[i][j].setFont(font);
//添加监听
button[i][j].addActionListener(this);
p1.add(button[i][j]);
}
}
//面板添加到窗口当中
add(p1, BorderLayout.CENTER);
//副面板--存放显示的提示信息
JPanel p2 = new JPanel();
promptLabel = new JLabel();
//颜色--红色
promptLabel.setForeground(Color.RED);
promptLabel.setText("提示:单击某一个按钮,作为马的起始位置");
p2.add(promptLabel);
add(p2, BorderLayout.SOUTH);
setSize(500, 500);
this.setLocationRelativeTo(null);
}
public static void main(String[] args) {
boardSize = 5;
new TestChess().setVisible(true);
}
static boolean DFS(int x, int y) {
//经过了所有的空位置
if (count == boardSize * boardSize) {
return true;
}
//遍历从当前点走日字能够到达的8个点
for (int dir = 0; dir < 8; dir++) {
//走日字到达下一个点
int next_x = x + dx[dir];
int next_y = y + dy[dir];
//超出边界
if (next_x < 1 || next_x > boardSize || next_y < 1 || next_y > boardSize) {
continue;
}
if (!button[next_x][next_y].getText().equals("")) {
continue;
}
//访问过的点计数
count++;
button[next_x][next_y].setText(String.valueOf(count));
if (DFS(next_x, next_y)) {
return true;
}
//从日字的一个对角线出发不能到达所有的位置
count--;//后退一步
button[next_x][next_y].setText("");
}
return false;
}
@Override
public void actionPerformed(ActionEvent e) {
String buttonKey = e.getActionCommand();
int intKey = Integer.parseInt(buttonKey);
int row = intKey / boardSize + 1;
int col = intKey % boardSize;
if(col==0){
col=boardSize;
row=intKey/boardSize;
}
button[row][col].setText("1");
if (DFS(row, col)) {
promptLabel.setText("提示:遍历成功");
} else {
promptLabel.setText("提示:遍历失败");
}
}
}
代码实现------有蹩脚
package TraversalOfHorseInChess;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @author Programming Bear
* @date 2021/06/22 15:36
**/
public class TestChess2 extends JFrame implements ActionListener {
//棋盘的按钮
static JButton button[][];
//棋盘大小(行数/列数)
static int boardSize;
//落子的顺序
static int count;
//预留位置
static int obstacleNum;
//马走日子的偏坐标
static int dx[] = {1, 2, 2, 1, -1, -2, -2, -1};
static int dy[] = {2, 1, -1, -2, -2, -1, 1, 2};
//提示信息标签
JLabel promptLabel = new JLabel();
public TestChess2() {
count = 1;
//定义font为微软雅黑,普通,24号
Font font = new Font("宋体", Font.PLAIN, 24);
this.setTitle("象棋中马的遍历");
//正式面板
JPanel p1 = new JPanel();
//GridLayout为网格布局
p1.setLayout(new GridLayout(boardSize, boardSize));
button = new JButton[boardSize + 1][boardSize + 1];
int n = 1;
for (int i = 1; i <= boardSize; i++) {
for (int j = 1; j <= boardSize; j++) {
button[i][j] = new JButton();
//动作命令,并非按钮上面的字,区别是哪个按钮
button[i][j].setActionCommand(String.valueOf(n++));
button[i][j].setFont(font);
//添加监听
button[i][j].addActionListener(this);
p1.add(button[i][j]);
}
}
button[1][1].setText("卒");
button[5][1].setText("卒");
obstacleNum = 2;
//面板添加到窗口当中
add(p1, BorderLayout.CENTER);
JPanel p2 = new JPanel();
promptLabel = new JLabel();
promptLabel.setForeground(Color.RED);
promptLabel.setText("提示:单击某一个按钮,作为马的起始位置");
p2.add(promptLabel);
add(p2, BorderLayout.SOUTH);
setSize(500, 500);
this.setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
boardSize = 5;
new TestChess2().setVisible(true);
}
static boolean DFS(int x, int y) {
//经过了所有的空位置
if (count == boardSize * boardSize - obstacleNum) {
return true;
}
//遍历从当前点走日字能够到达的8个点
for (int dir = 0; dir < 8; dir++) {
//走日字到达下一个点
int next_x = x + dx[dir];
int next_y = y + dy[dir];
//超出边界
if (next_x < 1 || next_x > boardSize || next_y < 1 || next_y > boardSize) {
continue;
}
if (!button[next_x][next_y].getText().equals("")) {
continue;
}
//访问过的点计数
count++;
button[next_x][next_y].setText(String.valueOf(count));
if (DFS(next_x, next_y)) {
return true;
}
//从日字的一个对角线出发不能到达所有的位置
count--;//后退一步
button[next_x][next_y].setText("");
}
return false;
}
@Override
public void actionPerformed(ActionEvent e) {
String buttonKey = e.getActionCommand();
int intKey = Integer.parseInt(buttonKey);
int row = intKey / boardSize + 1;
int col = intKey % boardSize;
if(col==0){
col=boardSize;
row=intKey/boardSize;
}
button[row][col].setText("1");
if (DFS(row, col)) {
promptLabel.setText("提示:遍历成功");
} else {
promptLabel.setText("提示:遍历失败");
}
}
}