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/snake.cpp

558 lines
10 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 <qwidget.h>
#include "snake.h"
int opposite[4] = { S, N , W, E };
int emptySq[4][4]={
{ N, E, W, N },
{ S, W, E, S },
{ E, N, S, E },
{ W, S, N, W }
};
Snake::Snake(Board *b, PixServer *p, Gate g, PixMap x)
{
list.setAutoDelete( true );
pixServer = p;
board = b;
gate = g;
pixmap = x;
random.setSeed(0);
}
void Snake::updateSamy()
{
int x = tail();
while ( x > 0) {
*list.at(x) = *list.at(x-1);
--x;
}
}
void Snake::zero()
{
for ( Samy *sam = list.first(); sam != 0; sam = list.next() ) {
board->set(sam->index, empty);
pixServer->erase(sam->index);
}
}
void Snake::appendSamy()
{
Samy *sam = new Samy;
list.append(sam);
updateSamy();
grow--;
}
void Snake::reset(int index, int border)
{
Samy *sam = list.first();
switch (border) {
case N:
sam->pixmap = (tail() == 0 ? HtailUp : HeadUp);
break;
case S:
sam->pixmap = (tail() == 0 ? HtailDown : HeadDown);
break;
case E:
sam->pixmap = (tail() == 0 ? HtailRight : HeadRight);
break;
case W:
sam->pixmap = (tail() == 0 ? HtailLeft : HeadLeft);
break;
}
sam->index = index;
sam->direction = border;
if (tail() > 1) {
sam = list.next();
if (sam->direction == border) {
if (border == N || border == S)
sam->pixmap = BodyVt;
else
sam->pixmap = BodyHz;
}
else {
if (border == W && sam->direction == S
|| border == N && sam->direction == E)
sam->pixmap = AngleNw;
if (border == E && sam->direction == S
|| border == N && sam->direction == W)
sam->pixmap = AngleNe;
if(border == W && sam->direction == N
|| border == S && sam->direction == E)
sam->pixmap = AngleSw;
if(border == E && sam->direction == N
|| border == S && sam->direction == W)
sam->pixmap = AngleSe;
}
} //end if (tail() > 1)
if (tail() > 0) {
sam = list.last();
switch (list.at(tail()-1)->direction) {
case N:
sam->pixmap = TailUp;
break;
case S:
sam->pixmap = TailDown;
break;
case E:
sam->pixmap = TailRight;
break;
case W:
sam->pixmap = TailLeft;
break;
}
}
}
void Snake::repaint( bool dirty)
{
int x = 0;
for ( Samy *sam = list.first(); sam != 0; sam = list.next(), x++) {
if (sam->index != OUT ) {
if(!dirty && x > 1 && x < tail())
continue;
pixServer->draw(sam->index, pixmap, sam->pixmap);
}
}
if (!growing() && hold != OUT && hold != gate) {
pixServer->erase(hold);
}
}
CompuSnake::CompuSnake( Board *b, PixServer *p)
: Snake( b, p, NORTH_GATE, CompuSnakePix )
{
init();
}
bool CompuSnake::init()
{
if( !list.isEmpty()) {
list.clear();
}
int index = NORTH_GATE;
int length = 12;
grow = 0;
hold = OUT;
if ( !board->isBrick(gate) ) return false;
Samy *sam;
for ( int x = 0; x < length; x++) {
board->set(index, snake);
sam = new Samy;
sam->direction = S;
sam->index = index;
sam->pixmap = (x == 0 ? HeadDown : BodyVt);
list.append(sam);
index = -1;
}
return true;
}
bool CompuSnake::permission()
{
if( list.isEmpty() ){
if ( hold != OUT) {
emit killed();
hold = OUT;
}
if(board->isBrick(gate)){
static int skip = 12;
if (skip < 12) {
skip++;
return false;
} else {
skip = 0;
return init();
}
}
else return false;
}
else return true;
}
void CompuSnake::nextMove()
{
if (!permission())
return;
Samy *sam = list.first();
int index = sam->index;
int dir = sam->direction;
static bool varies = false;
bool found = false;
for ( int x = 0; x < 4 ; x++) {
int next = board->getNext(x, sam->index);
if (board->isApple(next)){
index = next;
dir = x;
found = true;
grow+=6;
emit score(false, index);
break;
}
}
if(!found)
for ( int x = 0; x < 4 ; x++) {
int sq = emptySq[sam->direction][x];
if (varies && (x > 0 && x < 3))
sq = opposite[sq];
int next = board->getNext(sq, sam->index);
if (findEmpty(next, x)) {
index = next;
dir = sq;
found = true;
break;
}
}
varies = !varies;
if(!found) {
hold = list.last()->index;
if (board->isSnake(hold)) board->set(hold, empty);
removeSamy();
}
else
if(growing())
appendSamy();
else
if (!growing() && found) {
hold = list.last()->index;
if (board->isSnake(hold)) board->set(hold, empty);
updateSamy();
}
if( !list.isEmpty()) {
board->set(index, snake);
reset(index, dir);
}
if ( hold == gate)
out();
}
void KillerCompuSnake::nextMove()
{
if (!permission()) return;
Samy *sam = list.first();
int index = sam->index;
int dir = sam->direction;
static bool varies = false;
bool found = false;
if(!found) {
int sn = board->samyHeadIndex();
if(sn != -1 && board->isHead(sn)) {
int nextSq = board->getNextCloseTo(index, sn, false, lastIndex);
if(nextSq != -1) {
dir = board->direction(index, nextSq);
index = nextSq;
found = true;
}
}
}
if(!found)
for ( int x = 0; x < 4 ; x++) {
int sq = emptySq[sam->direction][x];
if (varies && (x > 0 && x < 3))
sq = opposite[sq];
int next = board->getNext(sq, sam->index);
if (findEmpty(next, x)) {
index = next;
dir = sq;
found = true;
break;
}
}
varies = !varies;
if(!found) {
hold = list.last()->index;
if (board->isSnake(hold)) board->set(hold, empty);
removeSamy();
}
else {
lastIndex = index;
if(growing())
appendSamy();
else
if (!growing() && found) {
hold = list.last()->index;
if (board->isSnake(hold)) board->set(hold, empty);
updateSamy();
}
}
if( !list.isEmpty()) {
board->set(index, snake);
reset(index, dir);
}
if ( hold == gate)
out();
}
void EaterCompuSnake::nextMove()
{
if (!permission()) return;
Samy *sam = list.first();
int index = sam->index;
int dir = sam->direction;
static bool varies = false;
bool found = false;
for ( int x = 0; x < 4 ; x++) {
int next = board->getNext(x, sam->index);
if (board->isApple(next)){
index = next;
dir = x;
found = true;
grow+=6;
emit score(false, index);
break;
}
}
if(!found) {
int sn;
bool apple = false;
for(sn = board->count() - 1; sn > 0; sn --) {
if(board->isApple(sn)) {
apple = true;
int nextSq = board->getNextCloseTo(index, sn, false);
if(nextSq != -1) {
dir = board->direction(index, nextSq);
index = nextSq;
found = true;
break;
}
}
}
if(!found && !apple) {
// No more apples, move snake to gate.
int nextSq = board->getNextCloseTo(index, gate, false, lastIndex);
if(nextSq != -1) {
dir = board->direction(index, nextSq);
index = nextSq;
found = true;
}
}
}
if(!found)
for ( int x = 0; x < 4 ; x++) {
int sq = emptySq[sam->direction][x];
if (varies && (x > 0 && x < 3))
sq = opposite[sq];
int next = board->getNext(sq, sam->index);
if (findEmpty(next, x)) {
index = next;
dir = sq;
found = true;
break;
}
}
varies = !varies;
if(!found) {
hold = list.last()->index;
if (board->isSnake(hold)) board->set(hold, empty);
removeSamy();
}
else {
lastIndex = index;
if(growing())
appendSamy();
else
if (!growing() && found) {
hold = list.last()->index;
if (board->isSnake(hold)) board->set(hold, empty);
updateSamy();
}
}
if( !list.isEmpty()) {
board->set(index, snake);
reset(index, dir);
}
if ( hold == gate)
out();
}
bool CompuSnake::findEmpty(int i, int it)
{
bool found = false;
bool change = false;
static int s_random = random.getLong(BoardWidth/2);
static int moves = 0;
if (moves > s_random) {
s_random = random.getLong(BoardWidth/2);
moves = 0;
change = true;
}
found = ( ( board->isEmpty(i) && it > 0)
|| ( board->isEmpty(i) && !change && it == 0) );
moves++;
change = false;
return found;
}
void CompuSnake::removeSamy()
{
list.remove();
grow = 0;
}
void CompuSnake::out()
{
emit closeGate( gate );
if( list.isEmpty() )
return;
if(list.first()->index == OUT) {
emit restartTimer();
list.clear();
}
}
SamySnake::SamySnake( Board *b, PixServer *p)
: Snake( b, p, SOUTH_GATE, SamyPix )
{
}
void SamySnake::init()
{
if( !list.isEmpty()) {
list.clear();
}
Samy *sam;
int index = SOUTH_GATE;
int length = 12;
grow = 0;
hold = 0;
for ( int x = 0; x < length; x++) {
board->set(index, head);
sam = new Samy;
sam->direction = N;
sam->index = index;
sam->pixmap = (x == 0 ? HeadUp : BodyVt);
list.append(sam);
index = -1;
}
}
samyState SamySnake::nextMove(int direction)
{
Samy *sam = list.first();
if(!board->isHead(sam->index) && sam->index != OUT)
return ko;
if ( direction == opposite[sam->direction])
direction = sam->direction;
if(sam->index == gate || sam->index == OUT )
direction = N;
if (sam->index == NORTH_GATE) {
emit goingOut();
direction = N;
}
int index = board->getNext(direction, sam->index);
if (board->isApple(index)) {
grow+=6;
emit score(true, index);
}
else if (!board->isEmpty(index))
return ko;
if(growing())
appendSamy();
else {
hold = list.last()->index;
board->set(hold, empty);
updateSamy();
}
board->set(sam->index, snake);
reset(index, direction);
board->set(index, head);
if ( hold == gate)
emit closeGate( gate );
else if ( hold == NORTH_GATE)
return out;
return ok;
}
#include "snake.moc"