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.
597 lines
13 KiB
597 lines
13 KiB
/***************************************************************************
|
|
lskatproc.cpp - description
|
|
-------------------
|
|
begin : Sun Apr 9 2000
|
|
copyright : (C) 2000 by Martin Heni
|
|
email : martin@heni-online.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. *
|
|
* *
|
|
***************************************************************************/
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "lskatproc.h"
|
|
|
|
#define MIN_TIME 1000 // usec
|
|
|
|
// ------------ class game ---------------------------------
|
|
lgame::lgame()
|
|
{
|
|
int i;
|
|
for (i=0;i<14;i++) cardvalues[i]=0;
|
|
cardvalues[(int)Ace]=11;
|
|
cardvalues[(int)Ten]=10;
|
|
cardvalues[(int)King]=4;
|
|
cardvalues[(int)Queen]=3;
|
|
cardvalues[(int)Jack]=2;
|
|
curmove[0]=-1;
|
|
curmove[1]=-1;
|
|
score[0]=0;
|
|
score[1]=0;
|
|
level=0;
|
|
endgame=false;
|
|
for (i=0;i<NO_OF_CARDS;i++) played[i]=0;
|
|
}
|
|
|
|
lgame::lgame(lgame &game)
|
|
{
|
|
*this=game;
|
|
}
|
|
|
|
lgame &lgame::operator=(lgame &game)
|
|
{
|
|
int i;
|
|
currentplayer=game.currentplayer;
|
|
startplayer=game.startplayer;
|
|
trump=game.trump;
|
|
movenumber=game.movenumber;
|
|
score[0]=game.score[0];
|
|
score[1]=game.score[1];
|
|
curmove[0]=game.curmove[0];
|
|
curmove[1]=game.curmove[1];
|
|
for (i=0;i<NO_OF_CARDS;i++) played[i]=game.played[i];
|
|
for (i=0;i<NO_OF_CARDS;i++) card[i]=game.card[i];
|
|
for (i=0;i<16;i++) cardheight[i]=game.cardheight[i];
|
|
endgame=game.endgame;
|
|
level=game.level;
|
|
return *this;
|
|
}
|
|
|
|
int lgame::MakeMove(int c,int pos)
|
|
{
|
|
int h;
|
|
curmove[currentplayer]=c;
|
|
h=GetHeight(currentplayer,pos);
|
|
if (currentplayer==startplayer)
|
|
{
|
|
movenumber++;
|
|
SetHeight(currentplayer,pos,h-1);
|
|
currentplayer=1-startplayer;
|
|
}
|
|
else
|
|
{
|
|
if (!LegalMove(curmove[startplayer],c)) return -1;
|
|
SetHeight(currentplayer,pos,h-1);
|
|
if (WonMove(curmove[startplayer],curmove[1-startplayer]))
|
|
{
|
|
// switch startplayer
|
|
startplayer=1-startplayer;
|
|
}
|
|
currentplayer=startplayer;
|
|
score[startplayer]+=CardValue(curmove[0]);
|
|
score[startplayer]+=CardValue(curmove[1]);
|
|
|
|
if (movenumber==NO_OF_TILES)
|
|
{
|
|
endgame=true;
|
|
return 2;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void lgame::Init()
|
|
{
|
|
int player,i,h,j,card;
|
|
// check what cards are played
|
|
for (player=0;player<2;player++)
|
|
{
|
|
for (i=0;i<8;i++)
|
|
{
|
|
h=GetHeight(player,i);
|
|
for (j=h;j<2;j++)
|
|
{
|
|
card=GetCard(player,i,j+1);
|
|
if (card>=0) played[card]=1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add value of both cards to the score of startplayer
|
|
void lgame::AddScore(int c1,int c2)
|
|
{
|
|
score[startplayer]+=CardValue(c1);
|
|
score[startplayer]+=CardValue(c2);
|
|
}
|
|
// Switch the startplayer
|
|
void lgame::SwitchStartplayer()
|
|
{
|
|
startplayer=1-startplayer;
|
|
}
|
|
|
|
// pos=0..7, player=0..1
|
|
void lgame::SetHeight(int player, int pos,int h)
|
|
{
|
|
int i;
|
|
i=8*player+pos;
|
|
cardheight[i]=h;
|
|
}
|
|
bool lgame::LegalMove(int p1, int p2)
|
|
{
|
|
CCOLOUR col1,col2,col3;
|
|
CCARD card1,card2,card3;
|
|
card1=(CCARD)((p1)/4);
|
|
col1=(CCOLOUR)((p1)%4);
|
|
card2=(CCARD)((p2)/4);
|
|
col2=(CCOLOUR)((p2)%4);
|
|
|
|
// force trump colour
|
|
if (card1==Jack) col1=trump;
|
|
if (card2==Jack) col2=trump;
|
|
|
|
// same colour always ok
|
|
if (col1==col2) return true;
|
|
|
|
// Search for same colour
|
|
bool flag=true;
|
|
for (int i=0;i<8;i++)
|
|
{
|
|
int h,c;
|
|
h=GetHeight(1-startplayer,i);
|
|
if (h==0) continue;
|
|
c=GetCard(1-startplayer,i,h);
|
|
card3=(CCARD)((c)/4);
|
|
col3=(CCOLOUR)((c)%4);
|
|
if (card3==Jack) col3=trump;
|
|
|
|
if (col3==col1)
|
|
{
|
|
flag=false;
|
|
break;
|
|
}
|
|
}
|
|
if (flag) return true;
|
|
|
|
|
|
return false;
|
|
}
|
|
int lgame::CardValue(int card)
|
|
{
|
|
int card1;
|
|
|
|
card1=card/4;
|
|
return cardvalues[card1];
|
|
}
|
|
int lgame::WonMove(int c1,int c2)
|
|
{
|
|
CCOLOUR col1,col2;
|
|
CCARD card1,card2;
|
|
|
|
card1=(CCARD)((c1)/4);
|
|
col1=(CCOLOUR)((c1)%4);
|
|
card2=(CCARD)((c2)/4);
|
|
col2=(CCOLOUR)((c2)%4);
|
|
|
|
// Two jacks
|
|
if (card1==Jack && card2==Jack)
|
|
{
|
|
if (col1<col2) return 0;
|
|
else return 1;
|
|
}
|
|
// One Jack wins always
|
|
if (card1==Jack) return 0;
|
|
if (card2==Jack) return 1;
|
|
|
|
// higher one wins if same colour
|
|
if (col1==col2)
|
|
{
|
|
if (card1==Ten)
|
|
{
|
|
if (card2==Ace) return 1;
|
|
else return 0;
|
|
}
|
|
if (card2==Ten)
|
|
{
|
|
if (card1==Ace) return 0;
|
|
return 1;
|
|
}
|
|
|
|
if ((int)card1<(int)card2) return 0;
|
|
return 1;
|
|
}
|
|
// trump wins
|
|
if (col1==trump) return 0;
|
|
if (col2==trump) return 1;
|
|
|
|
// first one wins
|
|
return 0;
|
|
|
|
}
|
|
// pos=0..7, height=2..1..(0 no card left), player=0..1
|
|
int lgame::GetCard(int player, int pos,int height)
|
|
{
|
|
int i;
|
|
if (height==0) return -1;
|
|
height=2-height;
|
|
|
|
i=NO_OF_TILES*player+8*height+pos;
|
|
return card[i];
|
|
}
|
|
|
|
|
|
|
|
// pos=0..7, player=0..1
|
|
int lgame::GetHeight(int player, int pos)
|
|
{
|
|
int i;
|
|
i=8*player+pos;
|
|
return cardheight[i];
|
|
}
|
|
|
|
// Returns a value for the given side
|
|
// applies all rules
|
|
int lgame::Subvalue(int side)
|
|
{
|
|
int sc,card;
|
|
int i,h,c;
|
|
CCOLOUR col1;
|
|
CCARD card1;
|
|
int trum1;
|
|
int jack1;
|
|
int havecol[4];
|
|
bool haveten[4];
|
|
bool haveace[4];
|
|
bool havejack[4];
|
|
|
|
sc=0;
|
|
trum1=0;
|
|
for (i=0;i<4;i++)
|
|
{
|
|
havecol[i]=false;
|
|
haveten[i]=false;
|
|
haveace[i]=false;
|
|
havejack[i]=false;
|
|
}
|
|
jack1=0;
|
|
for (i=0;i<8;i++)
|
|
{
|
|
h=GetHeight(side,i);
|
|
c=GetCard(side,i,h);
|
|
if (c<0) continue;
|
|
|
|
card1=(CCARD)((c)/4);
|
|
col1=(CCOLOUR)((c)%4);
|
|
|
|
if (col1==trump) trum1++;
|
|
havecol[(int)col1]++;
|
|
if (card1==Ten)
|
|
{
|
|
haveten[(int)col1]=true;
|
|
}
|
|
else if (card1==Ace)
|
|
{
|
|
haveace[(int)col1]=true;
|
|
}
|
|
else if (card1==Jack)
|
|
{
|
|
havejack[(int)col1]=true;
|
|
jack1++;
|
|
}
|
|
if (col1!=trump)
|
|
{
|
|
if (card1==Seven) sc-=60;
|
|
if (card1==Eight) sc-=50;
|
|
if (card1==Nine) sc-=40;
|
|
if (card1==Queen) sc-=10;
|
|
}
|
|
}
|
|
for (i=0;i<4;i++)
|
|
{
|
|
if (havecol[i]==0 && i!=trump) sc+=1000;
|
|
if (havecol[i]>5) sc+=800;
|
|
|
|
if (haveten[i]&&havecol[i]<2)
|
|
{
|
|
card=8*i+Ace;
|
|
if (!played[card] && !haveace[i]) sc-=2500; // free ten
|
|
}
|
|
if (haveace[i]) sc+=1500; // ace
|
|
if (havejack[i])
|
|
{
|
|
if (trump==Grand) sc+=4000+300*(4-i);
|
|
else sc+=2700+100*(4-i);
|
|
}
|
|
}
|
|
// evaluate
|
|
sc+=trum1*2500;
|
|
if (trum1==0) sc-=7000;
|
|
else if (trum1==1) sc-=5000;
|
|
return sc;
|
|
}
|
|
|
|
int lgame::Value(int player)
|
|
{
|
|
int sc;
|
|
sc=0;
|
|
|
|
// Someone won?
|
|
if (score[0]>90) sc+=90000;
|
|
else if (score[0]>60) sc+=70000;
|
|
else if (score[0]==60) sc+=40000;
|
|
|
|
if (score[1]>90) sc-=90000;
|
|
else if (score[1]>60) sc-=70000;
|
|
else if (score[1]==60) sc-=40000;
|
|
|
|
// Reward points
|
|
sc+=(score[0]-score[1])*650;
|
|
|
|
// Calulate cards
|
|
sc+=Subvalue(0);
|
|
sc-=Subvalue(1);
|
|
|
|
// random
|
|
sc+=random(500)-250;
|
|
|
|
if (player==1) return -sc;
|
|
return sc;
|
|
}
|
|
|
|
|
|
// -------------class lskatproc ----------------------------
|
|
lskatproc::lskatproc()
|
|
: KInputChildProcess(4096)
|
|
{
|
|
|
|
initrandom();
|
|
}
|
|
|
|
lskatproc::~lskatproc(){
|
|
}
|
|
|
|
|
|
|
|
bool lskatproc::ReceiveMsg(KEMessage* msg,int id)
|
|
{
|
|
// time_t timee,timea;
|
|
short x,y;
|
|
|
|
SendDebug("Receiv Msg");
|
|
// end of process
|
|
if (msg->HasKey(TQCString("Terminate")))
|
|
{
|
|
Terminate();
|
|
}
|
|
// Init of process
|
|
if (msg->HasKey(TQCString("Init")))
|
|
{
|
|
// No init necessary
|
|
}
|
|
// Make a move
|
|
if (msg->HasKey(TQCString("Cards")))
|
|
{
|
|
SendDebug("Process HasKey(Cards)");
|
|
// new game object
|
|
lgame game;
|
|
// extract data from message
|
|
game.ExtractGame(msg);
|
|
game.Init(); // must be AFTER ExtractGame
|
|
|
|
// Debug stuff only
|
|
sprintf(buf,"Trump=%d move=%d sc1=%d sc2=%d",
|
|
game.trump,game.curmove[1-game.currentplayer],game.score[0],game.score[1]);
|
|
SendDebug(buf);
|
|
|
|
if (game.currentplayer==0 && game.startplayer==0)
|
|
sprintf(buf,"+++ Computer ACTS as player ONE\n");
|
|
else if (game.currentplayer==0 && game.startplayer==1)
|
|
sprintf(buf,"+++ Computer REACTS as player ONE\n");
|
|
else if (game.currentplayer==1 && game.startplayer==1)
|
|
sprintf(buf,"+++ Computer ACTS as player TWO\n");
|
|
else
|
|
sprintf(buf,"+++ Computer REACTS as player TWO\n");
|
|
SendDebug(buf);
|
|
|
|
// fills data
|
|
x=0;y=0;
|
|
GetComputerMove(game,x,y,0);
|
|
sprintf(buf,"Computer move player=%d x=%d y=%d",game.currentplayer,x,y);
|
|
SendDebug(buf);
|
|
|
|
|
|
// report move
|
|
msg->RemoveAll();
|
|
msg->AddData(TQCString("Move"),game.currentplayer);
|
|
msg->AddData(TQCString("MoveX"),x);
|
|
msg->AddData(TQCString("MoveY"),y);
|
|
|
|
//timee=time(0);
|
|
// Sleep a minimum amount to slow down moves
|
|
//if ( 1000*(timee-timea) < MIN_TIME) usleep((MIN_TIME-1000*(timee-timea)));
|
|
SendDebug("Sending move back to main");
|
|
|
|
if (!IsTerminated()) SendMsg(msg);
|
|
fflush(stdout); // I do not know why it is needed..send does it too?
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------------------------- */
|
|
/* Computer Routinen */
|
|
/* --------------------------------------------------------------------------- */
|
|
|
|
// extract game from msg
|
|
int lgame::ExtractGame(KEMessage *msg)
|
|
{
|
|
int i;
|
|
short tmp;
|
|
char *p;
|
|
int size;
|
|
|
|
msg->GetData(TQCString("Startplayer"),startplayer);
|
|
msg->GetData(TQCString("CurrentPlayer"),currentplayer);
|
|
msg->GetData(TQCString("Cards"),p,size);
|
|
msg->GetData(TQCString("Level"),level);
|
|
level--; // start with level 0
|
|
for (i=0;i<NO_OF_CARDS;i++)
|
|
{
|
|
card[i]=((int *)p)[i];
|
|
}
|
|
msg->GetData(TQCString("Height"),p,size);
|
|
for (i=0;i<NO_OF_TILES;i++)
|
|
{
|
|
cardheight[i]=((int *)p)[i];
|
|
}
|
|
msg->GetData(TQCString("Trump"),tmp);
|
|
trump=(CCOLOUR)tmp;
|
|
short mm;
|
|
msg->GetData(TQCString("CurrentMove"),mm);
|
|
curmove[1-currentplayer]=(int)mm;
|
|
curmove[currentplayer]=-1;
|
|
msg->GetData(TQCString("No"),movenumber);
|
|
msg->GetData(TQCString("Sc1"),score[0]);
|
|
msg->GetData(TQCString("Sc2"),score[1]);
|
|
return 1;
|
|
}
|
|
|
|
long lgame::random(long max)
|
|
{
|
|
double value;
|
|
int r;
|
|
r=rand();
|
|
value=(double)((double)r*(double)max)/(double)RAND_MAX;
|
|
return (long)value;
|
|
}
|
|
|
|
void lskatproc::initrandom()
|
|
{
|
|
srand( (unsigned)time( NULL ) ); // randomize
|
|
}
|
|
|
|
|
|
int lskatproc::GetComputerMove(lgame game,short &x,short &y,int rek)
|
|
{
|
|
int i,maxvalue,maxmove,h,c;
|
|
//short oldscore;
|
|
bool startflag;
|
|
int startplayer;
|
|
int value;
|
|
lgame cgame;
|
|
char sbuf[100];
|
|
short mx,my;
|
|
|
|
|
|
for (i=0;i<2*rek;i++) sbuf[i]=' ';
|
|
sbuf[2*rek]=0;
|
|
|
|
x=0;
|
|
y=0;
|
|
if (game.currentplayer==game.startplayer) startflag=true;
|
|
else startflag=false;
|
|
|
|
startplayer=game.startplayer;
|
|
|
|
maxmove=0;
|
|
maxvalue=LOWERT;
|
|
|
|
sprintf(buf,"%s:Prepareing computer move (cur=%d) startflag=%d",
|
|
sbuf,game.currentplayer,startflag);
|
|
//SendDebug(buf);
|
|
for (i=0;i<8;i++)
|
|
{
|
|
sprintf(buf,"%s:Checking for card %d of player %d\n",sbuf,i,game.currentplayer);
|
|
// SendDebug(buf);
|
|
cgame=game;
|
|
h=cgame.GetHeight(cgame.currentplayer,i);
|
|
if (h<1)
|
|
{
|
|
sprintf(buf,"%s:i=%d:: no cards left",sbuf,i);
|
|
// SendDebug(buf);
|
|
continue; // no cards left
|
|
}
|
|
c=cgame.GetCard(cgame.currentplayer,i,h);
|
|
if (cgame.MakeMove(c,i)<0)
|
|
{
|
|
sprintf(buf,"%s:i=%d:: no legal move c1=%d c2=%d",
|
|
sbuf,i,cgame.curmove[cgame.startplayer],c);
|
|
// SendDebug(buf);
|
|
continue; // wrong card
|
|
}
|
|
if (!startflag) // we are second
|
|
{
|
|
sprintf(buf,"LEVEL %d %d",cgame.level,rek);
|
|
SendDebug(buf);
|
|
// Still recursion necessary and game not yet ended?
|
|
if (rek<2*cgame.level && !cgame.endgame)
|
|
{
|
|
// If we have the same startplayer the movesequence
|
|
// is not switched and we can take the negative value
|
|
// otherwise we play again, and have to take the poitiv value
|
|
if (cgame.startplayer==startplayer)
|
|
{
|
|
value=-GetComputerMove(cgame,mx,my,rek+1);
|
|
// if (value==-LOWERT) value=LOWERT; // no move possible
|
|
}
|
|
else
|
|
value=GetComputerMove(cgame,mx,my,rek+1);
|
|
}
|
|
else // evaluate position
|
|
{
|
|
value=cgame.Value(1-startplayer);
|
|
}
|
|
}
|
|
else // we are first player
|
|
{
|
|
// Alwayss the other player moves now
|
|
value=-GetComputerMove(cgame,mx,my,rek+1);
|
|
}
|
|
|
|
sprintf(buf,"%s:i=%d:: Value=%d",sbuf,i,value);
|
|
SendDebug(buf);
|
|
|
|
if (value>maxvalue)
|
|
{
|
|
maxvalue=value;
|
|
maxmove=i;
|
|
}
|
|
}
|
|
x=maxmove%4;
|
|
y=maxmove/4;
|
|
return maxvalue;
|
|
}
|
|
|
|
void lskatproc::SendDebug(const char *s)
|
|
{
|
|
KEMessage *msg=new KEMessage;
|
|
msg->AddData(TQCString("Debug"),s);
|
|
// msg->AddData("KLogSendMsg","debug.log");
|
|
// DEBUG
|
|
// SendMsg(msg);
|
|
// printf("%s\n",s);
|
|
|
|
delete msg;
|
|
}
|