I\'ve been searching for hours and haven\'t found a fully working solution for this kind of puzzle yet. So I followed similar problem with bishops.
What I need to do is
First I define my basic concept attack-ability. Attack-ability is how many knights can attack given cell.
Ex: Corner cells can be attacked by only two knights so attack-ability is two. Attack-ability of middle cell is 8.
Attack-ability of cells
| 2 | 3 | 4 | 4 | 4 | 4 | 3 | 2 |
| 3 | 4 | 6 | 6 | 6 | 6 | 4 | 3 |
| 4 | 6 | 8 | 8 | 8 | 8 | 6 | 4 |
| 4 | 6 | 8 | 8 | 8 | 8 | 6 | 4 |
| 4 | 6 | 8 | 8 | 8 | 8 | 6 | 4 |
| 4 | 6 | 8 | 8 | 8 | 8 | 6 | 4 |
| 3 | 4 | 6 | 6 | 6 | 6 | 4 | 3 |
| 2 | 3 | 4 | 4 | 4 | 4 | 3 | 2 |
Calculating attackability
AttackingNodes::AttackingNodes(int x, int y)
{
target = new Element(x, y);
CreateAtackList();
}
void AttackingNodes::CreateAtackList()
{
for(int diffx = -2; diffx <=2; ++diffx)
{
for(int diffy = -2; diffy <=2; ++diffy)
{
if((diffx*diffx + diffy* diffy) == 5)
{
AddAttack(target->_X + diffx, target->_Y + diffy);
}
}
}
}
void AttackingNodes::AddAttack( int x, int y )
{
if(x >= 0 && y >= 0 && x < BOARD_SIZE && y < BOARD_SIZE)
{
Element* element = new Element(x, y);
attackers.push_back(element);
}
}
size of the attackers in attacking nodes is equal to attackability.
Then multimap is created attackability against attackingNodes
for(int x = 0; x < BOARD_SIZE; ++x)
{
for(int y = 0; y < BOARD_SIZE; ++y)
{
AttackingNodes* nodes = new AttackingNodes(x, y);
attackNodes[x][y] = nodes;
mapAttackPriority.insert(std::make_pair(nodes->attackers.size(), nodes));
}
}
If attackability is low then there is lesser options for attacking given cell.
So first node from multimap is chosen which has lesser attacking options.
First cell will be 0, 0. There is two options to attack 0, 0
1, 2 or 2, 1
Lets choose 1, 2 and attack cells if they are empty. It can attack 6 cells.
attack(..) is placing knight in given cell. Atackers and targets are same way related. So data generated while calculating attack-ability is used here.
bool Solution::attack( Element* nodes )
{
++knightCount;
AttackingNodes* attackList = PriorityTargets::inst->attackNodes[nodes->_X][nodes->_Y];
std::list::iterator itr;
board[nodes->_X][nodes->_Y] = CC_KNIGHT;
for(itr = attackList->attackers.begin(); itr != attackList->attackers.end(); ++itr)
{
Element* attackNode = *itr;
if(board[attackNode->_X][attackNode->_Y] == CC_EMPTY)
{
board[attackNode->_X][attackNode->_Y] = CC_ATTACKED;
}
}
return false;
}
| A | 0 | A | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | A | 0 | 0 | 0 | 0 |
| 0 | K | 0 | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | A | 0 | 0 | 0 | 0 |
| A | 0 | A | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Then algorithm search for next empty cell (no Knight, no Attacked) with lowest attackability attack it with available options.
AttackingNodes* PriorityTargets::GetNextNode( Solution* solution )
{
std::multimap::iterator priorityItr;
for(priorityItr = mapAttackPriority.begin(); priorityItr != mapAttackPriority.end(); ++priorityItr)
{
AttackingNodes* attackNodes = priorityItr->second;
if(solution->board[attackNodes->target->_X][attackNodes->target->_Y] == CC_EMPTY)
{
return attackNodes;
}
}
return NULL;
}
It will be an another corner node for second options and it will go on until knight count is larger than 12 or no empty cells.
knight count is larger than 12 it is a fail attempt and backed tracked. If there is no empty cell then it is a solution.
Solution::Solution()
{
Clear();
}
void Solution::Print()
{
std::cout << std::endl ;
for(int x = 0; x < BOARD_SIZE; ++x)
{
for(int y = 0; y < BOARD_SIZE; ++y)
{
std::cout << (int)board[x][y] << " ";
}
std::cout << std::endl ;
}
std::cout << std::endl ;
}
bool Solution::Solve( Solution* solution )
{
AttackingNodes* nextAttackingNode = PriorityTargets::inst->GetNextNode(solution);
if(nextAttackingNode != NULL)
{
Solution* newSolutioon = new Solution();
std::list::iterator itr;
for(itr = nextAttackingNode->attackers.begin(); itr != nextAttackingNode->attackers.end(); ++itr)
{
Element* attack = *itr;
*newSolutioon = *solution;
newSolutioon->attack(attack);
if(newSolutioon->knightCount < 13)
{
Solve(newSolutioon);
}
else
{
//Fail
newSolutioon->Clear();
}
}
delete newSolutioon;
}
else
{
std::cout << "Solved" << std::endl;
solution->Print();
}
return false;
}
void Solution::Clear()
{
memset(board, 0, BOARD_SIZE*BOARD_SIZE);
knightCount = 0;
}
And I got the answer in lesser than 500 ms in visual studio 2008 release mode. I have used 2 for Knight and 1 for attacked.