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.
tdegames/ksnake/board.cpp

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 {
tqFatal("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;
}
}
tqFatal("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);
}
}
tqFatal("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);
}
}