You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
331 lines
6.6 KiB
331 lines
6.6 KiB
/**
|
|
* Copyright Michel Filippi <mfilippi@sade.rhein-main.de>
|
|
* Robert Williams
|
|
* Andrew Chant <andrew.chant@utoronto.ca>
|
|
* André Luiz dos Santos <andre@netvision.com.br>
|
|
* Benjamin Meyer <ben+ksnake@meyerhome.net>
|
|
*
|
|
* This file is part of the ksnake package
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <tqrect.h>
|
|
#include "board.h"
|
|
|
|
Pos::Pos(Pos *p, int i, int r) {
|
|
_parent = p;
|
|
_index = i;
|
|
_price = r;
|
|
left = right = next = fnext = 0;
|
|
inList = false;
|
|
}
|
|
Pos::~Pos() {
|
|
delete fnext;
|
|
}
|
|
int Pos::index() const {
|
|
return(_index);
|
|
}
|
|
void Pos::setPrice(int p) {
|
|
_price = p;
|
|
}
|
|
int Pos::price() const {
|
|
return(_price);
|
|
}
|
|
void Pos::setParent(Pos *p) {
|
|
_parent = p;
|
|
}
|
|
Pos *Pos::parent() const {
|
|
return(_parent);
|
|
}
|
|
Pos *Pos::listNext() {
|
|
inList = false;
|
|
return(next);
|
|
}
|
|
void Pos::addBTree(Pos *np) {
|
|
// Check direction np is going to.
|
|
Pos **p = 0;
|
|
if(np->index() < index())
|
|
p = &left;
|
|
else if(np->index() > index())
|
|
p = &right;
|
|
else {
|
|
qFatal("Repeated nodes on btree should never happens");
|
|
}
|
|
|
|
if(! *p) {
|
|
*p = np;
|
|
}
|
|
else {
|
|
(*p)->addBTree(np);
|
|
}
|
|
}
|
|
Pos *Pos::searchBTree(int i) {
|
|
if(i == index()) {
|
|
return(this);
|
|
}
|
|
else if(i < index() && left) {
|
|
return(left->searchBTree(i));
|
|
}
|
|
else if(right) {
|
|
return(right->searchBTree(i));
|
|
}
|
|
// Node not found.
|
|
return(0);
|
|
}
|
|
void Pos::addFList(Pos *np) {
|
|
np->fnext = fnext;
|
|
fnext = np;
|
|
}
|
|
void Pos::addList(Pos *np) {
|
|
if(np->inList)
|
|
return; // We're already in list.
|
|
|
|
np->inList = true;
|
|
Pos *p, *n;
|
|
for(p = this; p; p = n) {
|
|
// Check if the node next to p has a higher price.
|
|
n = p->next;
|
|
if(! n) {
|
|
// The new node go to tail.
|
|
np->next = 0;
|
|
p->next = np;
|
|
return;
|
|
}
|
|
|
|
if(np->price() <= n->price()) {
|
|
// Add new node after p.
|
|
np->next = p->next;
|
|
p->next = np;
|
|
return;
|
|
}
|
|
}
|
|
qFatal("Shouldn't reach this point");
|
|
}
|
|
|
|
Board::Board(int s)
|
|
:TQMemArray<int> (s)
|
|
{
|
|
sz = s;
|
|
samyIndex = -1;
|
|
}
|
|
|
|
void Board::index(int i)
|
|
{
|
|
row = i/BoardWidth;
|
|
col = i-(row*BoardWidth);
|
|
}
|
|
|
|
bool Board::inBounds(int i)
|
|
{
|
|
return ( i < 0 || i > sz-1 ? false : true);
|
|
}
|
|
|
|
void Board::set(int i, Square sq)
|
|
{
|
|
if (inBounds(i))
|
|
at(i) = sq;
|
|
if(sq == head)
|
|
samyIndex = i;
|
|
}
|
|
|
|
TQRect Board::rect(int i)
|
|
{
|
|
index(i);
|
|
return (TQRect(col*BRICKSIZE, row*BRICKSIZE, BRICKSIZE, BRICKSIZE));
|
|
}
|
|
|
|
bool Board::isEmpty(int i)
|
|
{
|
|
if (inBounds(i))
|
|
return (at(i) == empty ? true : false);
|
|
return true;
|
|
}
|
|
|
|
bool Board::isBrick(int i)
|
|
{
|
|
if (inBounds(i))
|
|
return (at(i) == brick ? true : false);
|
|
return false;
|
|
}
|
|
|
|
bool Board::isApple(int i)
|
|
{
|
|
if (inBounds(i))
|
|
return (at(i) == Apple ? true : false);
|
|
return false;
|
|
}
|
|
|
|
bool Board::isHead(int i)
|
|
{
|
|
if (inBounds(i))
|
|
return (at(i) == head ? true : false);
|
|
return false;
|
|
}
|
|
|
|
bool Board::isSnake(int i)
|
|
{
|
|
if (inBounds(i))
|
|
return (at(i) == snake ? true : false);
|
|
return false;
|
|
}
|
|
|
|
int Board::getNext(int n, int i)
|
|
{
|
|
index(i);
|
|
|
|
switch(n)
|
|
{
|
|
case NW:
|
|
return( i >= BoardWidth && col > 0 ? (i-BoardWidth)-1 : OUT);
|
|
case N:
|
|
return( i >= BoardWidth ? i-BoardWidth : OUT );
|
|
case NE:
|
|
return( i >= BoardWidth && col < BoardWidth-1 ? (i-BoardWidth)+1 : OUT);
|
|
case W:
|
|
return(col > 0 ? i-1 : OUT );
|
|
case E:
|
|
return(col < BoardWidth-1 ? i+1 : OUT );
|
|
case SW:
|
|
return( row < sz-BoardWidth && col > 0 ? (i+BoardWidth)-1 : OUT);
|
|
case S:
|
|
return( row < sz-BoardWidth ? i+BoardWidth : OUT );
|
|
case SE:
|
|
return( row < sz-BoardWidth && col < BoardWidth-1 ? (i+BoardWidth)+1 : OUT);
|
|
default:
|
|
return OUT;
|
|
}
|
|
}
|
|
|
|
int Board::getNextCloseToDumb(int s, int d)
|
|
{
|
|
if(s == d)
|
|
return(-1); // What can I say, we're here! ;o)
|
|
|
|
int nextSq = getNext(direction(s, d), s);
|
|
|
|
if(! isEmpty(nextSq))
|
|
return(-1);
|
|
|
|
return(nextSq);
|
|
}
|
|
|
|
int Board::getNextCloseTo(int s, int d, bool diag, int lastIndex)
|
|
{
|
|
if(s == d)
|
|
return(-1); // What can I say, we're here! ;o)
|
|
|
|
const int firstN = diag ? 4 : 0;
|
|
const int lastN = diag ? 8 : 4;
|
|
|
|
Pos *root = new Pos(0, s, 0), *list = root;
|
|
|
|
// List of indexes.
|
|
for(; list; list = list->listNext()) {
|
|
Pos *p;
|
|
|
|
// Check if current list node is the destination position.
|
|
if(list->index() == d) {
|
|
// Find first movement after root.
|
|
for(; ; list = p) {
|
|
p = list->parent();
|
|
if(p == root) {
|
|
// This is our move.
|
|
int nextSq = list->index();
|
|
delete root;
|
|
index(nextSq);
|
|
return(nextSq);
|
|
}
|
|
}
|
|
qFatal("Never here");
|
|
}
|
|
|
|
// Make possible moves.
|
|
for(int n = firstN; n < lastN; n ++) {
|
|
int i = getNext(n, list->index());
|
|
int pri = list->price() + 1;
|
|
|
|
// getNext returned valid place?
|
|
if(! inBounds(i) || (! isEmpty(i) && i != d)) {
|
|
// Or place is out of map or it's not empty,
|
|
// so go to the next possible move.
|
|
continue;
|
|
}
|
|
|
|
int pi = list->parent() ? list->parent()->index() : lastIndex;
|
|
if(pi != -1 && direction(pi, list->index()) !=
|
|
direction(list->index(), i)) {
|
|
pri += 10;
|
|
}
|
|
|
|
// Check if position wasn't processed yet.
|
|
if( (p = root->searchBTree(i))) {
|
|
// Position already processed.
|
|
// Check price of found position with current one.
|
|
if(p->price() > pri) {
|
|
// We found a cheapear way to reach the same
|
|
// place, so let's change the parent and price of p.
|
|
p->setPrice(list->price() + 1);
|
|
p->setParent(list);
|
|
list->addList(p);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Create new Pos class instance.
|
|
p = new Pos(list, i, pri);
|
|
|
|
// Add.
|
|
list->addList(p);
|
|
root->addFList(p);
|
|
root->addBTree(p);
|
|
}
|
|
}
|
|
|
|
// Solution not found.
|
|
delete root;
|
|
return(-1);
|
|
}
|
|
|
|
int Board::direction(int s, int d)
|
|
{
|
|
index(s);
|
|
int scol = col, srow = row;
|
|
index(d);
|
|
if(scol > col) { // Left.
|
|
if(srow < row)
|
|
return(SW);
|
|
else if(srow > row)
|
|
return(NW);
|
|
else
|
|
return(W);
|
|
}
|
|
else if(scol < col) { // Right.
|
|
if(srow < row)
|
|
return(SE);
|
|
else if(srow > row)
|
|
return(NE);
|
|
else
|
|
return(E);
|
|
}
|
|
else { // X's the same.
|
|
if(srow < row)
|
|
return(S);
|
|
else
|
|
return(N);
|
|
}
|
|
}
|