Knight's Tour Generator
Tourneys and the Fast Generation and Obfuscation of Closed Knight's Tours
Board.cpp
Go to the documentation of this file.
1 
4 // MIT License
5 //
6 // Copyright (c) 2019 Ian Parberry
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to
10 // deal in the Software without restriction, including without limitation the
11 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 // sell copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 // IN THE SOFTWARE.
25 
26 #include "Board.h"
27 
28 #include "Defines.h"
29 #include "Includes.h"
30 #include "Graph.h"
31 
32 extern MoveDeltas g_vecDeltas;
33 
35 
37 } //constructor
38 
41 
43  CBaseBoard(n){
44 } //constructor
45 
49 
51  CBaseBoard(w, h){
52 } //constructor
53 
58 
59 CBoard::CBoard(int move[], UINT w, UINT h):
60  CBaseBoard(move, w, h){
61 } //constructor
62 
68 
70  int s0, d0, s1, d1; //source and destination cells
71 
72  r.GetEdge0(s0, d0); //get them
73  r.GetEdge1(s1, d1);
74 
75  return IsRail(s0, d0, s1, d1);
76 } //IsRail
77 
85 
86 bool CBoard::IsRail(int s0, int d0, int s1, int d1){
87  return
88  IsKnightMove(s0, d0) && IsKnightMove(s1, d1) && //primary knight's moves
89  IsKnightMove(s0, s1) && IsKnightMove(d0, d1) && //cross knight's moves
90  IsMove(s0, d0) && IsMove(s1, d1) && //primary moves are present
91  !IsMove(s0, s1) && !IsMove(d0, d1); //cross moves are absent
92 } //IsRail
93 
140 
141 void CBoard::FindRails(std::vector<CRail>& rails){
142  assert(IsDirected()); //safety
143 
144  for(int s0=0; s0<(int)m_nSize; s0++) //source of move 0
145  for(int d0: {m_nMove[s0], m_nMove2[s0]}){ //destination of move 0
146  const int i = GetMoveIndex(s0, d0); //index of move 0
147 
148  if(i >= 4) //move 0 is forwards wrt the first for-loop
149  for(int j=4; j<8; j++) //downwards cross move from s0
150  if(i != j){ //eliminate the only forwards move that isn't a rail
151  const int s1 = GetDest(s0, g_vecDeltas[j]); //source of move 1
152 
153  if(s1 != UNUSED) //move 1 stays on board
154  for(int d1: {m_nMove[s1], m_nMove2[s1]}) //destination of move 1
155  if(IsRail(s0, d0, s1, d1)) //we have a rail
156  rails.push_back(CRail(s0, d0, s1, d1, m_nWidth)); //record it
157  } //if
158  } //for
159 
160  //randomize the rail list by applying a pseudo-random permutation
161  //using the standard random permutation generation algorithm
162 
163  const int n = (int)rails.size(); //number of rails
164 
165  for(int i=0; i<n; i++) //yes, the indices on the next line are correct...
166  std::swap(rails[i], rails[m_cRandom.randn(i, n - 1)]); //...because math
167 } //FindRails
168 
176 
178  assert(IsDirected()); //safety
179 
180  int s0, d0, s1, d1; //rail vertices
181 
182  r.GetEdge0(s0, d0); //rail edge
183  r.GetEdge1(s1, d1); //rail edge
184 
185  //delete the moves that are there
186 
187  DeleteMove(s0, d0);
188  DeleteMove(s1, d1);
189 
190  //insert the cross moves
191 
192  InsertDirectedMove(s0, s1);
193  InsertDirectedMove(d0, d1);
194 } //Switch
195 
198 
200  assert(IsDirected()); //safety
201 
202  std::vector<CRail> rails; //rail list
203  FindRails(rails); //find rails and put them in the rail list
204 
205  for(CRail& r: rails) //for each rail
206  if(IsRail(r)) //if it's still a rail, that is, it hasn't been flipped
207  Switch(r); //flip it
208 } //Shatter
209 
213 
215  MakeDirected(); //need a directed board
216 
217  for(int i=0; i<16; i++)
218  Shatter();
219 
220  JoinUntilTour();
221 
222  MakeUndirected(); //make it undirected before returning
223 } //Obfuscate
224 
238 
240  std::vector<CRail> rails; //rail list.
241  FindRails(rails); //find rails and put them in the rail list
242 
243  //get tourney identifiers for each cell
244 
245  int* id = new int[m_nSize]; //tourney identifiers
246  const UINT numcycles = GetTourneyIds(id); //get tourney id for each cell
247  if(numcycles == 1)return true; //bail out, we're good
248 
249  //construct the rail graph
250 
251  bool* used = new bool[m_nSize];
252 
253  for(UINT i=0; i<m_nSize; i++)
254  used[i] = false;
255 
256  CGraph g(numcycles); //rail graph
257  UINT count = 0;
258  std::vector<UINT> vecEdgeToRail;
259 
260  for(auto& r: rails){ //for each rail
261  int src0, dest0, src1, dest1; //rail vertices
262 
263  r.GetEdge0(src0, dest0); //rail edge
264  r.GetEdge1(src1, dest1); //rail edge
265 
266  if(!used[src0] && !used[dest0] && !used[src1] && !used[dest1]){
267  const int idsrc0 = id[src0]; //id of tourney that src0 is in
268  const int iddest0 = id[dest0]; //id of tourney that dest0 is in
269 
270  const int idsrc1 = id[src1]; //id of tourney that src1 is in
271  const int iddest1 = id[dest1]; //id of tourney that dest1 is in
272 
273  if(idsrc0 == iddest0 && idsrc1 == iddest1 && idsrc0 != idsrc1){
274  //rail connects tourneys
275  g.InsertEdge(idsrc0, idsrc1); //add rail edge to rail graph
276  used[src0] = used[src1] = used[dest0] = used[dest1] = true;
277  vecEdgeToRail.push_back(count);
278  } //if
279  } //if
280 
281  count++;
282  } //for
283 
284  delete [] used;
285  delete [] id;
286 
287  //g.PrintGraph(); //uncomment to output a graph so you can make a figure
288 
289  //switch rails in a spanning tree of the rail graph
290 
291  std::vector<UINT> spanningforest; //rails in spanning forest of rail graph
292  const UINT numtrees = g.BFSF(spanningforest);
293 
294  for(UINT i: spanningforest){ //for each rail in the spanning forest
295  UINT j = vecEdgeToRail[i]; //index of rail in rail list
296  Switch(rails[j]); //switch a spanning tree rail
297  } //for
298 
299  return numtrees == 1;
300 } //Join
301 
304 
306  if(IsTour())return; //bail out, it's a knight's tour already
307 
308  //make board directed, if it isn't already
309 
310  bool bWasUndirected = false;
311 
312  if(IsUndirected()){
313  bWasUndirected = true;
314  MakeDirected();
315  } //if
316 
317  UINT numcycles; //number of cycles
318 
319  do{
320  numcycles = Join();
321  }while(numcycles > 1);
322 
323  //clean up and exit
324 
325  if(bWasUndirected) //if the board came in to this function undirected
326  MakeUndirected(); //make it undirected again
327 } //JoinUntilTour
bool InsertDirectedMove(int src, int dest)
Insert a directed move.
Definition: BaseBoard.cpp:501
int GetMoveIndex(int src, int dest)
Get move index.
Definition: BaseBoard.cpp:408
void Shatter()
Shatter tourneys into more tourneys.
Definition: Board.cpp:199
Base chessboard.
Definition: BaseBoard.h:69
Defines, enumerated types, and typedefs.
bool DeleteMove(int src, int dest)
Delete a move.
Definition: BaseBoard.cpp:528
bool IsDirected()
Directed board test.
Definition: BaseBoard.cpp:319
UINT GetTourneyIds(int *&id)
Get tourney identifier for each cell.
Definition: BaseBoard.cpp:599
int GetDest(int i, const MoveDelta &delta)
Get destination of move.
Definition: BaseBoard.cpp:393
void MakeDirected()
Make into a directed board.
Definition: BaseBoard.cpp:335
A rail.
Definition: Rail.h:41
bool Join()
Join cycles to reduce tourney size.
Definition: Board.cpp:239
Useful includes.
bool IsUndirected()
Undirected board test.
Definition: BaseBoard.cpp:327
UINT randn()
Get a random unsigned integer.
Definition: Random.cpp:49
UINT m_nSize
Board size in cells.
Definition: BaseBoard.h:75
bool IsRail(int s0, int d0, int s1, int d1)
Rail test.
Definition: Board.cpp:86
bool IsKnightMove(int i, int j)
Knight's move test.
Definition: BaseBoard.cpp:138
CBoard()
Constructor.
Definition: Board.cpp:36
int * m_nMove2
Secondary move table.
Definition: BaseBoard.h:78
void GetEdge0(int &src, int &dest)
Get first edge.
Definition: Rail.cpp:71
MoveDeltas g_vecDeltas
Move deltas for all possible knight's moves.
Definition: Helpers.cpp:68
void JoinUntilTour()
Join cycles to reduce tourney size.
Definition: Board.cpp:305
unsigned int UINT
Abbreviation for unsigned integer.
Definition: Defines.h:84
void InsertEdge(const UINT i, const UINT j)
Insert an edge.
Definition: Graph.cpp:172
Undirected multi-graph.
Definition: Graph.h:102
void GetEdge1(int &src, int &dest)
Get second edge.
Definition: Rail.cpp:80
#define UNUSED
Contents of unused square on the chessboard.
Definition: Defines.h:31
UINT BFSF(std::vector< UINT > &result)
Breadth-first spanning forest.
Definition: Graph.cpp:189
Header for the graph CGraph and its vertices CVertex and edges CEdge.
bool IsMove(int i, int j)
Move test.
Definition: BaseBoard.cpp:127
int * m_nMove
Primary move table.
Definition: BaseBoard.h:77
void Switch(CRail &r)
Switch a rail.
Definition: Board.cpp:177
Header for the chessboard CBoard.
bool IsTour()
Knight's tour test.
Definition: BaseBoard.cpp:240
std::vector< MoveDelta > MoveDeltas
Move deltas for knight's moves.
Definition: Helpers.h:43
void FindRails(std::vector< CRail > &rails)
Find all rails.
Definition: Board.cpp:141
void MakeUndirected()
Make into an undirected board.
Definition: BaseBoard.cpp:351
void Obfuscate()
Obfuscate function.
Definition: Board.cpp:214
UINT m_nWidth
Board width in cells.
Definition: BaseBoard.h:73
CRandom m_cRandom
PRNG.
Definition: BaseBoard.h:71