N皇后算法

  1. #[allow(unused_imports)]
  2. use std::env::args;
  3. #[allow(dead_code)]
  4. fn main() {
  5. let mut board_width = 0;
  6. for arg in args() {
  7. board_width = match arg.parse() {
  8. Ok(x) => x,
  9. _ => 0,
  10. };
  11. if board_width != 0 {
  12. break;
  13. }
  14. }
  15. if board_width < 4 {
  16. println!(
  17. "Running algorithm with 8 as a default. Specify an alternative Chess board size for \
  18. N-Queens as a command line argument.\n"
  19. );
  20. board_width = 8;
  21. }
  22. let board = match nqueens(board_width) {
  23. Ok(success) => success,
  24. Err(err) => panic!("{}", err),
  25. };
  26. println!("N-Queens {} by {} board result:", board_width, board_width);
  27. print_board(&board);
  28. }
  29. /*
  30. The n-Queens search is a backtracking algorithm. Each row of the Chess board where a Queen is
  31. placed is dependent on all earlier rows. As only one Queen can fit per row, a one-dimensional
  32. integer array is used to represent the Queen's offset on each row.
  33. */
  34. pub fn nqueens(board_width: i64) -> Result<Vec<i64>, &'static str> {
  35. let mut board_rows = vec![0; board_width as usize];
  36. let mut conflict;
  37. let mut current_row = 0;
  38. //Process by row up to the current active row
  39. loop {
  40. conflict = false;
  41. //Column review of previous rows
  42. for review_index in 0..current_row {
  43. //Calculate the diagonals of earlier rows where a Queen would be a conflict
  44. let left = board_rows[review_index] - (current_row as i64 - review_index as i64);
  45. let right = board_rows[review_index] + (current_row as i64 - review_index as i64);
  46. if board_rows[current_row] == board_rows[review_index]
  47. || (left >= 0 && left == board_rows[current_row])
  48. || (right < board_width as i64 && right == board_rows[current_row])
  49. {
  50. conflict = true;
  51. break;
  52. }
  53. }
  54. match conflict {
  55. true => {
  56. board_rows[current_row] += 1;
  57. if current_row == 0 && board_rows[current_row] == board_width {
  58. return Err("No solution exists for specificed board size.");
  59. }
  60. while board_rows[current_row] == board_width {
  61. board_rows[current_row] = 0;
  62. if current_row == 0 {
  63. return Err("No solution exists for specificed board size.");
  64. }
  65. current_row -= 1;
  66. board_rows[current_row] += 1;
  67. }
  68. }
  69. _ => {
  70. current_row += 1;
  71. if current_row as i64 == board_width {
  72. break;
  73. }
  74. }
  75. }
  76. }
  77. Ok(board_rows)
  78. }
  79. fn print_board(board: &[i64]) {
  80. for row in 0..board.len() {
  81. print!("{}\t", board[row as usize]);
  82. for column in 0..board.len() as i64 {
  83. if board[row as usize] == column {
  84. print!("Q");
  85. } else {
  86. print!(".");
  87. }
  88. }
  89. println!();
  90. }
  91. }
  92. #[cfg(test)]
  93. mod test {
  94. use super::*;
  95. fn check_board(board: &Vec<i64>) -> bool {
  96. for current_row in 0..board.len() {
  97. //Column review
  98. for review_index in 0..current_row {
  99. //Look for any conflict.
  100. let left = board[review_index] - (current_row as i64 - review_index as i64);
  101. let right = board[review_index] + (current_row as i64 - review_index as i64);
  102. if board[current_row] == board[review_index]
  103. || (left >= 0 && left == board[current_row])
  104. || (right < board.len() as i64 && right == board[current_row])
  105. {
  106. return false;
  107. }
  108. }
  109. }
  110. true
  111. }
  112. #[test]
  113. fn test_board_size_4() {
  114. let board = nqueens(4).expect("Error propagated.");
  115. assert_eq!(board, vec![1, 3, 0, 2]);
  116. assert!(check_board(&board));
  117. }
  118. #[test]
  119. fn test_board_size_7() {
  120. let board = nqueens(7).expect("Error propagated.");
  121. assert_eq!(board, vec![0, 2, 4, 6, 1, 3, 5]);
  122. assert!(check_board(&board));
  123. }
  124. }