Knight-Path 1.0.D018
|
00001 // 00002 // knight-path - shortest knight path between two squares 00003 // Copyright (C) 2011 Peter Miller 00004 // 00005 // This program is free software; you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation; either version 3 of the License, or (at 00008 // your option) any later version. 00009 // 00010 // This program is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 // General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License along 00016 // with this program. If not, see <http://www.gnu.org/licenses/>. 00017 // 00018 00019 #include <deque> 00020 00021 #include <knight-path/solver/maze.h> 00022 00023 00024 solver_maze::~solver_maze() 00025 { 00026 } 00027 00028 00029 solver_maze::solver_maze() : 00030 solver() 00031 { 00032 } 00033 00034 00035 solver_maze::pointer 00036 solver_maze::create(void) 00037 { 00038 return pointer(new solver_maze()); 00039 } 00040 00041 00042 solver::list_t 00043 solver_maze::solve(const position &from, const position &to) 00044 { 00045 if (from == to) 00046 { 00047 // handle the trivial case 00048 list_t result; 00049 result.push_back(to); 00050 return result; 00051 } 00052 00053 enum { unvisited = 99 }; 00054 00055 struct square 00056 { 00057 int distance; 00058 position predecessor; 00059 00060 square() : distance(unvisited) { } 00061 square(int d, const position &ap) : distance(d), predecessor(ap) { } 00062 }; 00063 00064 // 00065 // We model the chass board by annotating it with the distances 00066 // between each square and the starting point, and also with the 00067 // next point backwards in the path. 00068 // 00069 // The board array could be replaced by a std::map<position> however this 00070 // comes with a penalty: O(log n) rather than O(1). Conversely, the board 00071 // array means we need to be able to use the position::get_x and get_y 00072 // methods; a map would mean we could leave them private. Today we choose 00073 // speed, and thus choose grope otherwise private members. 00074 // 00075 square board[8][8]; 00076 board[from.get_y()][from.get_x()] = square(0, from); 00077 00078 // 00079 // Starting from the "from" position, look for additional places 00080 // reachable by knight moves. 00081 // 00082 std::deque<position> work; 00083 work.push_back(from); 00084 while (!work.empty()) 00085 { 00086 position p = work.front(); 00087 work.pop_front(); 00088 int dist = 1 + board[p.get_y()][p.get_x()].distance; 00089 00090 // ask for a list or reachable positions 00091 position::list_t candidates = p.knight_moves(); 00092 00093 // check each possible knight move 00094 for 00095 ( 00096 position::list_t::const_iterator it = candidates.begin(); 00097 it != candidates.end(); 00098 ++it 00099 ) 00100 { 00101 square &b = board[it->get_y()][it->get_x()]; 00102 if (b.distance == unvisited) 00103 { 00104 work.push_back(*it); 00105 b.distance = dist; 00106 b.predecessor = p; 00107 } 00108 else 00109 { 00110 // 00111 // Because we always push new work onto the back of the queue, 00112 // and we always pop the next job from the front of the 00113 // queue, it means that the work queue is always ordered from 00114 // shortest-path to longest-path. 00115 // 00116 assert(dist >= b.distance); 00117 #if 0 00118 if (dist < b.distance) 00119 { 00120 b.distance = dist; 00121 b.predecessor = p; 00122 } 00123 #endif 00124 } 00125 00126 // 00127 // The shortest-path implicit in the ordering means that when we 00128 // first reach the "to" square, we have found a shortest path -- 00129 // there may be others as short, and there may be others longer, but 00130 // we can stop when we first reach the "to" square. 00131 // 00132 if (*it == to) 00133 { 00134 work.clear(); 00135 break; 00136 } 00137 } 00138 00139 #if 0 00140 // 00141 // For debug, print the in-progress board, annotated with distances 00142 // 00143 std::cerr << __FILE__ << ": " << __LINE__ << ": p = " << p << std::endl; 00144 for (int y = 7; y >= 0; --y) 00145 { 00146 std::cerr << " +---+---+---+---+---+---+---+---+" << std::endl; 00147 std::cerr << (y + 1); 00148 std::cerr << " |"; 00149 for (int x = 0; x < 8; ++x) 00150 { 00151 const square &b = board[y][x]; 00152 std::cerr << ' '; 00153 if (b.distance == unvisited) 00154 std::cerr << ' '; 00155 else 00156 std::cerr << b.distance; 00157 std::cerr << " |"; 00158 } 00159 std::cerr << std::endl; 00160 } 00161 std::cerr << " `---+---+---+---+---+---+---+---'" << std::endl; 00162 std::cerr << " A B C D E F G H" << std::endl; 00163 #endif 00164 } 00165 00166 // 00167 // Build the answer by tracing the path backwards. 00168 // 00169 list_t result; 00170 position p = to; 00171 while (p != from) 00172 { 00173 square &b = board[p.get_y()][p.get_x()]; 00174 assert(b.distance != unvisited); 00175 result.push_front(p); 00176 p = b.predecessor; 00177 } 00178 return result; 00179 }