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/kjumpingcube/brain.cpp

622 lines
14 KiB

/* ****************************************************************************
This file is part of the game 'KJumpingCube'
Copyright (C) 1998-2000 by Matthias Kiefer
<matthias.kiefer@gmx.de>
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 program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**************************************************************************** */
#include "brain.h"
#include "cube.h"
#include <math.h>
#include <kapplication.h>
#undef DEBUG // uncomment this to get useful messages
#include <assert.h>
#ifdef DEBUG
#include <iostream.h>
#endif
#include "prefs.h"
Brain::Brain(int initValue)
{
setSkill(Prefs::EnumSkill::Beginner);
stopped=false;
active=false;
currentLevel=0;
// initialize the random number generator
random.setSeed(initValue);
}
void Brain::setSkill(int newSkill)
{
_skill=newSkill;
switch(_skill)
{
case Prefs::EnumSkill::Beginner:
maxLevel=1;
break;
case Prefs::EnumSkill::Average:
maxLevel=3;
break;
case Prefs::EnumSkill::Expert:
maxLevel=5;
break;
default:
break;
}
}
int Brain::skill() const
{
return _skill;
}
void Brain::stop()
{
stopped=true;
}
bool Brain::isActive() const
{
return active;
}
bool Brain::getHint(int& row, int& column,CubeBox::Player player ,CubeBox box)
{
if(isActive())
return false;
active=true;
stopped=false;
currentPlayer=player;
int i=0,j=0;
int moves=0; // how many moves are the favourable ones
CubeBox::Player opponent=(player==CubeBox::One)?CubeBox::Two : CubeBox::One;
// if more than one cube has the same rating this array is used to select
// one
coordinate* c2m=new coordinate[box.dim()*box.dim()];
// Array, which holds the assessment of the separate moves
double **worth=new double*[box.dim()];
for(i=0;i<box.dim();i++)
worth[i]=new double[box.dim()];
// alle Werte auf kleinstm<74>glichen Wert setzen
double min=-pow(2.0,sizeof(long int)*8-1); // Maximum auf kleinst m<>glichen Wert setzen
for(i=0;i<box.dim();i++)
for(j=0;j<box.dim();j++)
{
worth[i][j]=min;
}
// find the favourable cubes to increase
moves=findCubes2Move(c2m,player,box);
// if only one cube is found, then don't check recursively the move
if(moves==1)
{
#ifdef DEBUG
cerr << "found only one favourable cube" << endl;
#endif
row=c2m[0].row;
column=c2m[0].column;
}
else
{
#ifdef DEBUG
cerr << "found more than one favourable cube: " << moves << endl;
#endif
for(i=0;i<moves;i++)
{
// if Thinking process stopped
if(stopped)
{
#ifdef DEBUG
cerr << "brain stopped" << endl;
#endif
active=false;
for(i=0;i<box.dim();i++)
delete[] worth[i];
delete [] worth;
delete [] c2m;
return false;
}
#ifdef DEBUG
cerr << "checking cube " << c2m[i].row << "," << c2m[i].column << endl;
#endif
// for every found possible move, simulate this move and store the assessment
worth[c2m[i].row][c2m[i].column]=doMove(c2m[i].row,c2m[i].column,player,box);
#ifdef DEBUG
cerr << "cube " << c2m[i].row << "," << c2m[i].column << " : " << worth[c2m[i].row][c2m[i].column] << endl;
#endif
}
// find the maximum
double max=-1E99; // set max to minimum value
#ifdef DEBUG
cerr << "searching for the maximum" << endl;
#endif
for(i=0;i<moves;i++)
{
if(box[c2m[i].row][c2m[i].column]->owner()!=(Cube::Owner)opponent)
{
if(worth[c2m[i].row][c2m[i].column]>max )
{
max=worth[c2m[i].row][c2m[i].column];
}
}
}
#ifdef DEBUG
cerr << "found Maximum : " << max << endl;
#endif
// found maximum more than one time ?
int counter=0;
for(i=0;i<moves;i++)
{
#ifdef DEBUG
cerr << c2m[i].row << "," << c2m[i].column << " : " << worth[c2m[i].row][c2m[i].column] << endl;
#endif
if(worth[c2m[i].row][c2m[i].column]==max)
if(box[c2m[i].row][c2m[i].column]->owner() != (Cube::Owner)opponent)
{
c2m[counter].row=c2m[i].row;
c2m[counter].column=c2m[i].column;
counter++;
}
}
assert(counter>0);
// if some moves are equal, choose a random one
if(counter>1)
{
#ifdef DEBUG
cerr << "choosing a random cube: " << endl ;
#endif
counter=random.getLong(counter);
}
row=c2m[counter].row;
column=c2m[counter].column;
#ifdef DEBUG
cerr << "cube: " << row << "," << column << endl;
#endif
}
// clean up
for(i=0;i<box.dim();i++)
delete[] worth[i];
delete [] worth;
delete [] c2m;
active=false;
return true;
}
double Brain::doMove(int row, int column, CubeBox::Player player , CubeBox box)
{
double worth=0;
currentLevel++; // increase the current depth of recurse calls
// if the maximum depth isn't reached
if(currentLevel < maxLevel)
{
// test, if possible to increase this cube
if(!box.simulateMove(player,row,column))
{
currentLevel--;
return 0;
}
// if the player has won after simulating this move, return the assessment of the field
if(box.playerWon(player))
{
currentLevel--;
return (long int)pow((float)box.dim()*box.dim(),(maxLevel-currentLevel))*box.assessField(currentPlayer);
}
int i;
int moves=0;
// if more than one cube has the same rating this array is used to select
// one
coordinate* c2m=new coordinate[box.dim()*box.dim()];
// the next move has does the other player
player=(player==CubeBox::One)? CubeBox::Two : CubeBox::One;
// find the favourable cubes to increase
moves=findCubes2Move(c2m,player,box);
// if only one cube is found, then don't check recursively the move
if(moves==1)
{
box.simulateMove(player,c2m[0].row,c2m[0].column);
worth=(long int)pow((float)box.dim()*box.dim(),(maxLevel-currentLevel-1))*box.assessField(currentPlayer);
}
else
{
for(i=0;i<moves;i++)
{
kapp->processEvents();
// if thinking process stopped
if(stopped)
{
currentLevel--;
return 0;
}
// simulate every possible move
worth+=doMove(c2m[i].row,c2m[i].column,player,box);
}
}
delete [] c2m;
currentLevel--;
return worth;
}
else
{
// if maximum depth of recursive calls are reached, return the assessment
currentLevel--;
box.simulateMove(player,row,column);
return box.assessField(currentPlayer);
}
}
int Brain::findCubes2Move(coordinate *c2m,CubeBox::Player player,CubeBox& box)
{
int i,j;
int opponent=(player==CubeBox::One)? CubeBox::Two : CubeBox::One;
int moves=0;
int min=9999;
if(_skill==Prefs::EnumSkill::Beginner)
{
int max=0;
for(i=0;i<box.dim();i++)
for(j=0;j<box.dim();j++)
{
if(box[i][j]->owner() != opponent)
{
c2m[moves].row=i;
c2m[moves].column=j;
c2m[moves].val=box[i][j]->value();
if(c2m[moves].val>max)
max=c2m[moves].val;
moves++;
}
}
// find all moves with maximum value
int counter=0;
for(i=0;i<moves;i++)
{
if(c2m[i].val==max)
{
c2m[counter].row=c2m[i].row;
c2m[counter].column=c2m[i].column;
c2m[counter].val=c2m[i].val;
counter++;
}
}
if(counter!=0)
{
moves=counter;
}
}
else // if skill is not Beginner
{
int secondMin=min;
// put values on the cubes
for(i=0;i<box.dim();i++)
for(j=0;j<box.dim();j++)
{
// use only cubes, who don't belong to the opponent
if(box[i][j]->owner() != opponent)
{
int val;
// check neighbours of every cube
val=assessCube(i,j,player,box);
#ifdef DEBUG
if(currentLevel==0)
cerr << i << "," << j << " : " << val << endl;
#endif
// only if val >= 0 its a favourable move
if( val > 0 )
{
if(val<min)
{
secondMin=min;
min=val;
}
// store coordinates
c2m[moves].row=i;
c2m[moves].column=j;
c2m[moves].val=val;
moves++;
}
}
}
// If all cubes are bad, check all cubes for the next move
if(moves==0)
{
min=4;
for(i=0;i<box.dim();i++)
for(j=0;j<box.dim();j++)
{
if(box[i][j]->owner() != opponent)
{
c2m[moves].row=i;
c2m[moves].column=j;
c2m[moves].val=( box[i][j]->max() - box[i][j]->value() );
if(c2m[moves].val<min)
min=c2m[moves].val;
moves++;
}
}
}
int counter=0;
// find all moves with minimum assessment
for(i=0;i<moves;i++)
{
if(c2m[i].val==min)
{
c2m[counter].row=c2m[i].row;
c2m[counter].column=c2m[i].column;
c2m[counter].val=c2m[i].val;
counter++;
}
else if(_skill == Prefs::EnumSkill::Average)
{
if(c2m[i].val == secondMin)
{
c2m[counter].row=c2m[i].row;
c2m[counter].column=c2m[i].column;
c2m[counter].val=c2m[i].val;
counter++;
}
}
}
if(counter!=0)
{
moves=counter;
}
}
int maxMoves=10;
// if more than maxMoves moves are favourable, take maxMoves random moves
// because it will take to much time if you check all
if(moves > maxMoves)
{
// find maxMoves random cubes to move with
coordinate* tempC2M=new coordinate[maxMoves];
coordinate tmp={-1,-1,0};
for(i=0;i<maxMoves;i++)
tempC2M[i]=tmp;
// this array takes the random chosen numbers, so that no
// number will be taken two times
int *results=new int[moves];
for(i=0;i<moves;i++)
results[i]=0;
for(i=0;i<maxMoves;i++)
{
int temp;
do
{
temp=random.getLong(moves);
}
while(results[temp]!=0);
results[temp]=1;
tempC2M[i].row=c2m[temp].row;
tempC2M[i].column=c2m[temp].column;
tempC2M[i].val=c2m[temp].val;
}
delete [] results;
for(i=0;i<maxMoves;i++)
{
c2m[i].row=tempC2M[i].row;
c2m[i].column=tempC2M[i].column;
c2m[i].val=tempC2M[i].val;
}
delete [] tempC2M;
moves=maxMoves;
}
return moves;
}
int Brain::assessCube(int row,int column,CubeBox::Player player,CubeBox& box) const
{
int diff;
if(row==0) // first row
{
if(column == 0) // upper left corner
{
diff=getDiff(0,1,player,box) ;
int temp=getDiff(1,0,player,box);
if(temp < diff)
diff=temp;
}
else if(column == box.dim()-1) // upper right corner
{
diff=getDiff(0,column-1,player,box);
int temp=getDiff(1,column,player,box);
if(temp < diff)
diff=temp;
}
else
{
diff=getDiff(row,column-1,player,box);
int temp=getDiff(row,column+1,player,box);
if(temp < diff)
diff = temp;
temp=getDiff(row+1,column,player,box);
if(temp < diff)
diff = temp;
}
}
else if(row==box.dim()-1) // last row
{
if(column == 0) // lower left corner
{
diff=getDiff(row,1,player,box);
int temp=getDiff(row-1,0,player,box);
if(temp < diff)
diff=temp;
}
else if(column == box.dim()-1) // lower right corner
{
diff=getDiff(row,column-1,player,box);
int temp=getDiff(row-1,column,player,box);
if(temp < diff)
diff=temp;
}
else
{
diff=getDiff(row,column-1,player,box);
int temp=getDiff(row,column+1,player,box);
if(temp < diff)
diff = temp;
temp=getDiff(row-1,column,player,box);
if(temp < diff)
diff = temp;
}
}
else if(column == 0) // first column
{
diff = getDiff(row,1,player,box);
int temp = getDiff(row-1,0,player,box);
if(temp < diff)
diff = temp;
temp = getDiff(row+1,0,player,box);
if(temp < diff)
diff = temp;
}
else if(column == box.dim()-1) // last column
{
diff = getDiff(row,column-1,player,box);
int temp = getDiff(row-1,column,player,box);
if(temp < diff)
diff = temp;
temp = getDiff(row+1,column,player,box);
if(temp < diff)
diff = temp;
}
else
{
diff=getDiff(row-1,column,player,box);
int temp=getDiff(row+1,column,player,box);
if(temp < diff)
diff = temp;
temp=getDiff(row,column-1,player,box);
if(temp < diff)
diff = temp;
temp=getDiff(row,column+1,player,box);
if(temp < diff)
diff = temp;
}
int temp;
temp=( box[row][column]->max()-box[row][column]->value() );
int val;
val=diff-temp+1;
val=val*(temp+1);
return val;
}
int Brain::getDiff(int row,int column, CubeBox::Player player, CubeBox& box) const
{
int diff;
if(box[row][column]->owner() != (Cube::Owner)player)
{
diff=( box[row][column]->max() - box[row][column]->value() );
}
else
{
diff=( box[row][column]->max() - box[row][column]->value()+1 );
}
return diff;
}