2 Copyright 2004-2005 Andi Peredri <andi@ukr.net>
3 Copyright 2007-2008 Fela Winkelmolen <fela.kde@gmail.com>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "abstractgrid.h"
21 #include <cstdlib> // rand()
22 #include <unistd.h> // sleep()
28 AbstractCell::AbstractCell(int index
)
34 char *AbstractCell::toString() {
35 char *str
= new char[4];
36 str
[0] = (m_cables
& Left
) ? '-' : ' ';
37 str
[2] = (m_cables
& Right
) ? '-' : ' ';
38 if ((m_cables
& Up
) && (m_cables
& Down
)) {
40 } else if (m_cables
& Up
) {
42 } else if (m_cables
& Down
) {
44 } else if ((m_cables
& Left
) && (m_cables
& Right
)){
54 bool AbstractCell::isTerminal() const
56 return (m_cables
== Up
|| m_cables
== Right
57 || m_cables
== Down
|| m_cables
== Left
);
60 // only used to change the cables (not to rotate the cell!)
61 void AbstractCell::setCables(Directions newCables
)
63 m_cables
= originalCables
= newCables
;
64 m_hasBeenMoved
= false;
67 void AbstractCell::setServer(bool isServer
)
69 m_isServer
= isServer
;
72 void AbstractCell::setConnected(bool isConnected
)
74 m_isConnected
= isConnected
;
77 void AbstractCell::makeEmpty()
79 m_cables
= originalCables
= None
;
81 m_isConnected
= false;
82 m_hasBeenMoved
= false;
85 void AbstractCell::emptyMove()
87 m_hasBeenMoved
= true;
90 void AbstractCell::turnClockwise()
92 Directions newdirs
= None
;
93 if (m_cables
& Up
) newdirs
= Directions(newdirs
| Right
);
94 if (m_cables
& Right
) newdirs
= Directions(newdirs
| Down
);
95 if (m_cables
& Down
) newdirs
= Directions(newdirs
| Left
);
96 if (m_cables
& Left
) newdirs
= Directions(newdirs
| Up
);
98 m_hasBeenMoved
= true;
101 void AbstractCell::turnCounterclockwise()
103 Directions newdirs
= None
;
104 if (m_cables
& Up
) newdirs
= Directions(newdirs
| Left
);
105 if (m_cables
& Right
) newdirs
= Directions(newdirs
| Up
);
106 if (m_cables
& Down
) newdirs
= Directions(newdirs
| Right
);
107 if (m_cables
& Left
) newdirs
= Directions(newdirs
| Down
);
109 m_hasBeenMoved
= true;
112 void AbstractCell::invert()
118 void AbstractCell::reset()
120 m_cables
= originalCables
;
121 m_hasBeenMoved
= false;
128 AbstractGrid::~AbstractGrid()
130 while (!m_cells
.isEmpty()) delete m_cells
.takeFirst();
133 void AbstractGrid::initializeGrid(uint width
, uint height
, Wrapping wrapping
)
135 if ((width
* height
) != (m_width
* m_height
)) {
136 while (!m_cells
.isEmpty()) delete m_cells
.takeFirst();
138 for (uint index
= 0; index
< width
*height
; ++index
) {
139 m_cells
.append(newCell(index
));
145 m_isWrapped
= wrapping
;
149 while(hasUnneededCables() || solutionCount() != 1) {
150 // the grid is invalid: create a new one
155 for (uint i
= 0; i
< width
*height
; ++i
) {
156 int rotation
= rand() % 4; // 0..3
157 for (int j
= 0; j
< rotation
; ++j
) {
158 // ratate every cable clockwise
159 m_cells
[i
]->turnClockwise();
164 void AbstractGrid::print() {
169 for (uint r
= 0; r
< m_height
; ++r
) {
170 for (uint c
= 0; c
< m_width
; ++c
) {
171 str1
+= m_cells
[index
]->toString();
173 if (m_cells
[index
]->hasBeenMoved()) {
180 kDebug() << str1
<< " " << str2
;
186 // ============ auxiliary funciontions ===================== //
188 void AbstractGrid::createGrid()
190 for (uint i
= 0; i
< m_width
*m_height
; ++i
) {
191 m_cells
[i
]->makeEmpty();
194 // add a random server
195 server_index
= rand() % (m_width
*m_height
);
196 m_cells
[server_index
]->setServer(true);
198 // number of cells that aren't free
200 // TODO:use a global constant instead of 10 / 8
201 const uint MinimumNumCells
= m_width
*m_height
* 8 / 10;
202 // retries until the minimum number of cells is big enough
203 while (cellCount
< MinimumNumCells
) {
205 list
.append(server_index
);
206 if (rand() % 2) addRandomCable(list
);
208 // add some random cables...
209 // the list empties if there aren't many free cells left
210 // (because of addRandomCable() not doing anything)
211 while (!list
.isEmpty()) {
213 addRandomCable(list
);
214 if (rand() % 2) addRandomCable(list
);
215 list
.erase(list
.begin());
218 list
.append(list
.first());
219 list
.erase(list
.begin());
223 // count not empty cells
225 for (uint i
= 0; i
< m_width
*m_height
; ++i
) {
226 if (m_cells
[i
]->cables() != None
) ++cellCount
;
231 // adds a random direction and moves on (if possible)
232 void AbstractGrid::addRandomCable(QList
<uint
>& list
)
234 int cell
= list
.first();
235 // find all the cells surrounding list.first()
236 // (0 when cells don't exist)
237 int ucell
= uCell(cell
); // up
238 int rcell
= rCell(cell
); // right
239 int dcell
= dCell(cell
); // down
240 int lcell
= lCell(cell
); // left
242 QMap
<Directions
, int> freeCells
;
244 // of those cells map the ones that are free
245 if (ucell
!= NO_CELL
&& m_cells
[ucell
]->cables() == None
) {
246 freeCells
[Up
] = ucell
;
248 if (rcell
!= NO_CELL
&& m_cells
[rcell
]->cables() == None
) {
249 freeCells
[Right
] = rcell
;
251 if (dcell
!= NO_CELL
&& m_cells
[dcell
]->cables() == None
) {
252 freeCells
[Down
] = dcell
;
254 if (lcell
!= NO_CELL
&& m_cells
[lcell
]->cables() == None
) {
255 freeCells
[Left
] = lcell
;
258 if (freeCells
.isEmpty()) return; // no free cells left
260 QMap
<Directions
, int>::ConstIterator it
= freeCells
.constBegin();
261 // move the iterator to a random direction connecting to a free cell
262 for (int i
= rand() % freeCells
.count(); i
> 0; --i
) ++it
;
264 // add the cable in the direction of cell
265 Directions newCables
= Directions(m_cells
[cell
]->cables() | it
.key());
266 m_cells
[cell
]->setCables(newCables
);
267 // add a cable in the opposite direction, on the free cell
268 m_cells
[it
.value()]->setCables(invertDirection(it
.key()));
269 // append the cell that was free to the list
270 list
.append(it
.value());
273 int AbstractGrid::uCell(uint cell
) const
275 if (cell
>= m_width
) {
276 return cell
- m_width
;
277 } else if (m_isWrapped
) {
278 return m_width
* (m_height
- 1) + cell
;
284 int AbstractGrid::dCell(uint cell
) const
286 if (cell
< m_width
* (m_height
- 1)) {
287 return cell
+ m_width
;
288 } else if (m_isWrapped
) {
289 return cell
- m_width
* (m_height
- 1);
295 int AbstractGrid::lCell(uint cell
) const
297 if (cell
% m_width
> 0) {
299 } else if (m_isWrapped
) {
300 return cell
- 1 + m_width
;
306 int AbstractGrid::rCell(uint cell
) const
308 if (cell
% m_width
< m_width
- 1) {
310 } else if (m_isWrapped
) {
311 return cell
+ 1 - m_width
;
317 // TODO: something better to invert directions (remove duplication etc...)
318 Directions
AbstractGrid::invertDirection(Directions givenDirection
)
320 QMap
<Directions
, Directions
> invDirs
;
322 invDirs
[Right
] = Left
;
324 invDirs
[Left
] = Right
;
326 return invDirs
[givenDirection
];
330 int AbstractGrid::solutionCount()
332 MoveList possibleNextMoves
;
333 // TODO: put following in external function
334 foreach (AbstractCell
*cell
, m_cells
) {
335 if (!cell
->hasBeenMoved()) {
336 Directions dirs
= cell
->cables();
340 move
= Move(cell
->index(), Move::None
);
341 possibleNextMoves
.append(move
);
342 } else if (dirs
== (Up
| Down
) || dirs
== (Left
| Right
)) {
343 // cables forming a line
344 move
= Move(cell
->index(), Move::None
);
345 possibleNextMoves
.append(move
);
347 move
= Move(cell
->index(), Move::Left
);
348 possibleNextMoves
.append(move
);
350 // other kind of cables
351 move
= Move(cell
->index(), Move::None
);
352 possibleNextMoves
.append(move
);
354 move
= Move(cell
->index(), Move::Left
);
355 possibleNextMoves
.append(move
);
357 move
= Move(cell
->index(), Move::Right
);
358 possibleNextMoves
.append(move
);
360 move
= Move(cell
->index(), Move::Inverted
);
361 possibleNextMoves
.append(move
);
367 // all cells have been moved
368 if (possibleNextMoves
.isEmpty()) {
369 return isSolution() ? 1 : 0;
373 int solutionsFound
= 0;
374 foreach (Move nextMove
, possibleNextMoves
) {
375 int index
= nextMove
.index();
377 switch (nextMove
.move()) {
379 m_cells
[index
]->emptyMove();
382 m_cells
[index
]->turnClockwise();
385 m_cells
[index
]->turnCounterclockwise();
388 m_cells
[index
]->invert();
392 if (movesDoneArePossible()) {
393 solutionsFound
+= solutionCount(); // recursive call
396 m_cells
[index
]->reset(); // undo move
398 return solutionsFound
;
401 bool AbstractGrid::movesDoneArePossible()
404 foreach (AbstractCell
*cell
, m_cells
) {
405 if (!cell
->hasBeenMoved()) continue;
407 uint x
= cell
->index() % m_width
;
408 uint y
= cell
->index() / m_width
;
409 Directions cables
= cell
->cables();
411 // check if there are moved cells near the borders that are wrong
413 if (x
== 0 && cables
& Left
) return false;
414 if (x
== m_width
-1 && cables
& Right
) return false;
415 if (y
== 0 && cables
& Up
) return false;
416 if (y
== m_height
-1 && cables
& Down
) return false;
419 // check if there are contiguous moved cells that are wrong
422 int lcell
= lCell(cell
->index());
423 if (lcell
!= NO_CELL
&& m_cells
[lcell
]->hasBeenMoved()) {
424 // also the cell to the left of the current has been moved
426 // if it doesn't connect return false
427 if (!(m_cells
[lcell
]->cables() & Right
)) return false;
430 if (cables
& Right
) {
431 int rcell
= rCell(cell
->index());
432 if (rcell
!= NO_CELL
&& m_cells
[rcell
]->hasBeenMoved()) {
433 if (!(m_cells
[rcell
]->cables() & Left
)) return false;
437 int ucell
= uCell(cell
->index());
438 if (ucell
!= NO_CELL
&& m_cells
[ucell
]->hasBeenMoved()) {
439 if (!(m_cells
[ucell
]->cables() & Down
)) return false;
443 int dcell
= dCell(cell
->index());
444 if (dcell
!= NO_CELL
&& m_cells
[dcell
]->hasBeenMoved()) {
445 if (!(m_cells
[dcell
]->cables() & Up
)) return false;
454 bool AbstractGrid::hasUnneededCables()
456 foreach (AbstractCell
*cell
, m_cells
) {
457 if (cell
->isTerminal() || cell
->isServer() || cell
->cables() == None
) {
461 Directions oldCables
= cell
->cables();
462 cell
->setCables(None
);
464 bool solution
= isSolution();
465 cell
->setCables(oldCables
);
468 // it has a solution also when the cables of cell are removed
476 bool AbstractGrid::isSolution()
478 foreach (AbstractCell
*cell
, m_cells
) {
479 cell
->setConnected(false);
482 // return false if there is a terminal that isn't connected
483 foreach (AbstractCell
*cell
, m_cells
) {
484 if (cell
->isTerminal() && !cell
->isConnected()) {
488 // all terminals are connected
492 void AbstractGrid::updateConnections()
494 // TODO: add int AbstractGrid::cellsCount()
495 QVector
<bool> newConnections(m_width
* m_height
);
496 for (uint i
= 0; i
< m_width
* m_height
; ++i
) {
497 newConnections
[i
] = false;
500 // indexes of the changed cells
501 QList
<int> changedCells
;
502 changedCells
.append(server_index
);
503 m_cells
[server_index
]->setConnected(true);
505 while (!changedCells
.isEmpty())
507 int cell_index
= changedCells
.first();
508 int uindex
= uCell(cell_index
);
509 int rindex
= rCell(cell_index
);
510 int dindex
= dCell(cell_index
);
511 int lindex
= lCell(cell_index
);
513 AbstractCell
*cell
= m_cells
[cell_index
];
514 AbstractCell
*ucell
= (uindex
!= NO_CELL
) ? m_cells
[uindex
] : 0;
515 AbstractCell
*rcell
= (rindex
!= NO_CELL
) ? m_cells
[rindex
] : 0;
516 AbstractCell
*dcell
= (dindex
!= NO_CELL
) ? m_cells
[dindex
] : 0;
517 AbstractCell
*lcell
= (lindex
!= NO_CELL
) ? m_cells
[lindex
] : 0;
519 if ((cell
->cables() & Up
) && ucell
!= 0 &&
520 (ucell
->cables() & Down
) && !newConnections
[uindex
]) {
521 newConnections
[uindex
] = true;
522 changedCells
.append(ucell
->index());
524 if ((cell
->cables() & Right
) && rcell
!= 0 &&
525 (rcell
->cables() & Left
) && !newConnections
[rindex
]) {
526 newConnections
[rindex
] = true;
527 changedCells
.append(rcell
->index());
529 if ((cell
->cables() & Down
) && dcell
!= 0 &&
530 (dcell
->cables() & Up
) && !newConnections
[dindex
]) {
531 newConnections
[dindex
] = true;
532 changedCells
.append(dcell
->index());
534 if ((cell
->cables() & Left
) && lcell
!= 0 &&
535 (lcell
->cables() & Right
) && !newConnections
[lindex
]) {
536 newConnections
[lindex
] = true;
537 changedCells
.append(lcell
->index());
539 changedCells
.erase(changedCells
.begin());
542 for (uint i
= 0; i
< m_width
* m_height
; i
++){
543 m_cells
[i
]->setConnected(newConnections
[i
]);