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/libksirtet/common/ai.cpp

357 lines
8.9 KiB

#include "ai.h"
#include "ai.moc"
#include <assert.h>
#include <tqlabel.h>
#include <tqhbox.h>
#include <tqvbox.h>
#include <layout.h>
#include <tqgrid.h>
#include <tqwhatsthis.h>
#include <klocale.h>
#include <kconfig.h>
#include <kapplication.h>
#include "commonprefs.h"
#include "board.h"
#include "base/piece.h"
#include "base/factory.h"
//-----------------------------------------------------------------------------
AIPiece::AIPiece()
: _current(0)
{}
AIPiece::~AIPiece()
{
delete _current;
}
void AIPiece::init(const Piece *piece, Board *b)
{
_piece = piece;
_board = b;
if ( _current==0 ) _current = new Piece;
reset();
}
void AIPiece::reset()
{
curPos = 0;
curRot = 0;
if (_piece) _current->copy(_piece);
// else _current->generateNext(0);
nbRot = _current->nbConfigurations() - 1;
nbPos = _board->matrix().width() - _current->size().first + 1;
}
bool AIPiece::increment()
{
curPos++;
if ( curPos==nbPos ) {
if ( curRot==nbRot ) {
// if ( _piece || _current->type()==Piece::info().nbTypes() ) {
reset();
return false;
// }
// _current->generateNext(_current->type()+1);
// nbRot = _current->nbConfigurations() - 1;
// curRot = 0;
}
_current->rotate(true, TQPoint(0, 0));
nbPos = _board->matrix().width() - _current->size().first + 1;
curRot++;
curPos = 0;
}
return true;
}
bool AIPiece::place()
{
if ( curRot==3 ) {
if ( !_board->rotateRight() ) return false;
} else for (uint i=0; i<curRot; i++)
if ( !_board->rotateLeft() ) return false;
curDec = curPos - _board->currentPos().first - _current->min().first;
if ( curDec!=0 && _board->moveRight(curDec)!=(uint)kAbs(curDec) )
return false;
_board->dropDown();
return !_board->isGameOver();
}
//-----------------------------------------------------------------------------
const AI::Data AI::LastData = { 0, 0, 0, false, 0 };
AI::AI(uint tTime, uint oTime, const Data *DATA)
: timer(this), thinkTime(tTime), orderTime(oTime), stopped(false),
board(0)
{
connect(&timer, TQT_SIGNAL(timeout()), TQT_SLOT(timeout()));
for (uint i=0; DATA[i].name; i++) {
Element element;
element.coefficient = 0.;
element.trigger = 0;
element.data = &DATA[i];
_elements.append(element);
}
settingsChanged();
}
void AI::resizePieces(uint size)
{
uint oldSize = pieces.size();
for (uint i=size; i<oldSize; i++) delete pieces[i];
pieces.resize(size);
for (uint i=oldSize; i<size; i++) pieces[i] = new AIPiece;
}
AI::~AI()
{
delete board;
resizePieces(0);
}
void AI::initThink()
{
board->copy(*main);
}
void AI::launch(Board *m)
{
main = m;
if ( board==0 )
board = static_cast<Board *>(bfactory->createBoard(false, 0));
pieces[0]->init(main->currentPiece(), board); // current
if ( pieces.size()>=2 ) pieces[1]->init(main->nextPiece(), board); // next
state = Thinking;
hasBestPoints = false;
startTimer();
}
void AI::stop()
{
timer.stop();
stopped = true;
}
void AI::start()
{
if (stopped) {
startTimer();
stopped = false;
}
}
void AI::startTimer()
{
switch (state) {
case Thinking: timer.start(thinkTime, true); break;
case GivingOrders: timer.start(orderTime, true); break;
}
}
void AI::timeout()
{
switch (state) {
case Thinking:
if ( think() ) state = GivingOrders;
break;
case GivingOrders:
if ( emitOrder() ) return;
break;
}
startTimer();
}
bool AI::emitOrder()
{
if ( bestRot==3 ) {
bestRot = 0;
main->pRotateRight();
} else if (bestRot) {
bestRot--;
main->pRotateLeft();
} else if ( bestDec>0 ) {
bestDec--;
main->pMoveRight();
} else if ( bestDec<0 ) {
bestDec++;
main->pMoveLeft();
} else {
main->pDropDownStart();
return true;
}
return false;
}
bool AI::think()
{
initThink();
bool moveOk = true;
for (uint i=0; i<pieces.size(); i++)
if ( !pieces[i]->place() ) {
moveOk = false;
break;
}
if (moveOk) {
double p = points();
if ( !hasBestPoints || p>bestPoints
|| (p==hasBestPoints && random.getBool()) ) {
hasBestPoints = true;
bestPoints = p;
bestDec = pieces[0]->dec();
bestRot = pieces[0]->rot();
}
}
for (uint i=pieces.size(); i>0; i--)
if ( pieces[i-1]->increment() ) return false;
return true;
}
double AI::points() const
{
double pts = 0;
for (uint i=0; i<_elements.size(); i++) {
if ( _elements[i].coefficient==0.0 ) continue;
double v = _elements[i].data->function(*main, *board);
if ( _elements[i].data->triggered && tqRound(v)<_elements[i].trigger )
continue;
pts += _elements[i].coefficient * v;
}
return pts;
}
void AI::settingsChanged()
{
int d = CommonPrefs::thinkingDepth();
resizePieces(d);
for (uint i=0; i<_elements.size(); i++) {
const Data &data = *_elements[i].data;
_elements[i].coefficient = AIConfig::coefficient(data);
if (data.triggered) _elements[i].trigger = AIConfig::trigger(data);
}
if ( timer.isActive() ) launch(main);
}
double AI::nbOccupiedLines(const Board &, const Board &current)
{
return current.matrix().height() - current.nbClearLines();
}
double AI::nbHoles(const Board &, const Board &current)
{
uint nb = 0;
for (uint i=0; i<current.matrix().width(); i++) {
for (int j=current.firstColumnBlock(i)-1; j>=0; j--) {
KGrid2D::Coord c(i, j);
if ( current.matrix()[c]==0 ) nb++;
}
}
return nb;
}
double AI::peakToPeak(const Board &, const Board &current)
{
int min = current.matrix().height()-1;
for (uint i=0; i<current.matrix().width(); i++)
min = kMin(min, current.firstColumnBlock(i));
return (int)current.firstClearLine()-1 - min;
}
double AI::mean(const Board &, const Board &current)
{
double mean = 0;
for (uint i=0; i<current.matrix().width(); i++)
mean += current.firstColumnBlock(i);
return mean / current.matrix().width();
}
double AI::nbSpaces(const Board &main, const Board &current)
{
double nb = 0;
double m = mean(main, current);
for (uint i=0; i<current.matrix().width(); i++) {
int j = current.firstColumnBlock(i);
if ( j<m ) nb += m - j;
}
return nb;
}
double AI::nbRemoved(const Board &main, const Board &current)
{
return current.nbRemoved() - main.nbRemoved();
}
//-----------------------------------------------------------------------------
const uint AIConfig::minThinkingDepth = 1;
const uint AIConfig::maxThinkingDepth = 2;
AIConfig::AIConfig(const TQValueVector<AI::Element> &elements)
: TQWidget(0, "ai config")
{
TQGridLayout *top = new TQGridLayout(this, 3, 2, KDialog::marginHint(),
KDialog::spacingHint());
TQLabel *label = new TQLabel(i18n("Thinking depth:"), this);
top->addWidget(label, 0, 0);
KIntNumInput *in = new KIntNumInput(this, "kcfg_ThinkingDepth");
in->setRange(minThinkingDepth, maxThinkingDepth);
top->addWidget(in, 0, 1);
top->addRowSpacing(1, KDialog::spacingHint());
TQGrid *grid = new TQGrid(2, this);
top->addMultiCellWidget(grid, 2, 2, 0, 1);
for (uint i=0; i<elements.size(); i++) {
const AI::Data &data = *elements.at(i).data;
TQLabel *label = new TQLabel(i18n(data.label), grid);
if (data.whatsthis) TQWhatsThis::add(label, i18n(data.whatsthis));
label->setFrameStyle(TQFrame::Panel | TQFrame::Plain);
TQVBox *vb = new TQVBox(grid);
if (data.whatsthis) TQWhatsThis::add(vb, i18n(data.whatsthis));
vb->setMargin(KDialog::spacingHint());
vb->setSpacing(KDialog::spacingHint());
vb->setFrameStyle(TQFrame::Panel | TQFrame::Plain);
if (data.triggered) {
KIntNumInput *trig = new KIntNumInput(vb, triggerKey(data.name));
trig->setRange(0, 10, 1, true);
}
KDoubleNumInput *coeff = new KDoubleNumInput(vb, coefficientKey(data.name));
coeff->setRange(0.0, 1.0, 1.0, true);
}
}
TQCString AIConfig::triggerKey(const char *name)
{
return "kcfg_Trigger_" + TQCString(name);
}
TQCString AIConfig::coefficientKey(const char *name)
{
return "kcfg_Coefficient_" + TQCString(name);
}
double AIConfig::coefficient(const AI::Data &data)
{
KConfigSkeletonItem *item = CommonPrefs::self()->findItem( TQString("Coefficient_%1").arg(data.name) );
assert(item);
return item->property().toDouble();
}
int AIConfig::trigger(const AI::Data &data)
{
KConfigSkeletonItem *item = CommonPrefs::self()->findItem( TQString("Trigger_%1").arg(data.name) );
assert(item);
return item->property().toInt();
}