// 声明包为 main，表示这是一个可执行程序
package main

import (
	"fmt"  // 导入 fmt 包，用于格式化输入输出
	"time" // 导入 time 包，用于计算程序运行时间
)

// 定义常量 N，代表棋盘的大小为 8x8
const N = 8

var (
	// dx 和 dy 数组定义了马在棋盘上可以移动的8个方向的 x 和 y 坐标变化
	// 例如，(dx[0], dy[0]) = (2, 1) 表示马可以从 (x, y) 移动到 (x+2, y+1)
	dx = []int{2, 1, -1, -2, -2, -1, 1, 2}
	dy = []int{1, 2, 2, 1, -1, -2, -2, -1}
	// stepCount 用于记录算法关键步骤的执行次数，主要用于性能分析
	stepCount int64
)

// isValid 函数检查给定坐标 (x, y) 是否在棋盘内，并且该位置尚未被访问过
// board[x][y] == -1 表示该位置未被访问
func isValid(x, y int, board [][]int) bool {
	stepCount++ // 每次检查都增加关键步骤计数
	return x >= 0 && x < N && y >= 0 && y < N && board[x][y] == -1
}

// getDegree 函数计算从 (x, y) 位置出发，有多少个可行的下一步（即“出度”
// 这是 Warnsdorff 规则的核心，用于优化路径选择
func getDegree(x, y int, board [][]int) int {
	count := 0
	// 遍历8个可能的移动方向
	for i := 0; i < 8; i++ {
		// 如果移动后的位置是有效的，则计数器加一
		if isValid(x+dx[i], y+dy[i], board) {
			count++
		}
	}
	return count
}

// solveKnightTour 是解决马踏棋盘问题的主要递归函数
// x, y: 当前马的位置
// moveCount: 当前是第几步
// board: 棋盘状态
func solveKnightTour(x, y, moveCount int, board [][]int) bool {
	// 将当前位置标记为第 moveCount 步
	board[x][y] = moveCount

	// 如果已经走满了 N*N-1 步（步数从0开始），说明已经找到了一个完整的路径
	if moveCount == N*N-1 {
		return true // 返回 true 表示成功
	}

	// 定义一个结构体 Move，用于存储下一步的位置和该位置的“出度”
	type Move struct {
		x, y, degree int
	}
	// 创建一个切片，用于存储所有可能的下一步
	moves := make([]Move, 0, 8)

	// 遍历8个方向，找出所有有效的下一步
	for i := 0; i < 8; i++ {
		nx, ny := x+dx[i], y+dy[i]
		if isValid(nx, ny, board) {
			// 如果是有效的一步，计算该点的“出度”并存入 moves 切片
			degree := getDegree(nx, ny, board)
			moves = append(moves, Move{nx, ny, degree})
		}
	}

	// 使用选择排序对所有可能的下一步进行排序，按照“出度”从小到大排序
	// 这是 Warnsdorff 规则的应用：优先选择“出度”最少的点，这样可以减少后面出现“死路”的可能性
	for i := 0; i < len(moves); i++ {
		for j := i + 1; j < len(moves); j++ {
			if moves[j].degree < moves[i].degree {
				moves[i], moves[j] = moves[j], moves[i]
			}
		}
	}

	// 按照排序后的顺序，依次尝试每一个可能的下一步
	for _, move := range moves {
		// 递归调用 solveKnightTour，进入下一步
		if solveKnightTour(move.x, move.y, moveCount+1, board) {
			return true // 如果递归返回 true，说明找到了解，直接返回 true
		}
	}

	// 如果所有可能的下一步都无法导致一个成功的解，则进行“回溯”
	// 将当前位置重置为未访问状态 (-1)，并返回 false
	board[x][y] = -1
	return false
}

// printBoard 函数用于打印最终的棋盘路径
func printBoard(board [][]int) {
	fmt.Println("\n马踏棋盘路径:")
	for i := range N {
		for j := range N {
			// 使用 %3d 格式化输出，使棋盘对齐
			fmt.Printf("%3d", board[i][j])
		}
		fmt.Println() // 每行结束后换行
	}
}

// main 函数是程序的入口
func main() {
	var startX, startY int
	// 提示用户输入起始位置
	fmt.Print("请输入起始位置 (x y, 范围0-7): ")
	// 读取用户输入的坐标
	fmt.Scanf("%d %d", &startX, &startY)

	// 检查输入的坐标是否在有效范围内
	if startX < 0 || startX >= N || startY < 0 || startY >= N {
		fmt.Println("输入位置无效!")
		return // 如果无效，程序退出
	}

	// 初始化棋盘，创建一个 N*N 的二维切片
	board := make([][]int, N)
	for i := range board {
		board[i] = make([]int, N)
		// 将棋盘所有位置初始化为 -1，表示都未被访问
		for j := range board[i] {
			board[i][j] = -1
		}
	}

	// 重置关键步骤计数器
	stepCount = 0
	// 记录算法开始时间
	start := time.Now()

	// 调用 solveKnightTour 函数开始求解
	if solveKnightTour(startX, startY, 0, board) {
		// 如果求解成功
		elapsed := time.Since(start) // 计算总耗时
		printBoard(board)            // 打印棋盘
		fmt.Printf("\n求解成功!\n")
		fmt.Printf("运行时间: %v\n", elapsed)
		fmt.Printf("关键步骤执行次数: %d\n", stepCount)
	} else {
		// 如果求解失败
		fmt.Println("无解!")
	}
}
