213 lines
5.2 KiB
C++
213 lines
5.2 KiB
C++
/* -*- Mode: C -*- */
|
|
#define debugprint
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <vector>
|
|
|
|
#include <mcp.h>
|
|
|
|
#include "playerlib.h"
|
|
|
|
static int
|
|
send_move(int row, int col)
|
|
{
|
|
FILE *f = fdopen(dup(CHILD_OUT_FD), "w");
|
|
fprintf(f, "%u,%u", row, col);
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
buildTree(gameTree *currentNode, cell (*field)[fieldSize], movesList *moves, int depth, char enemyc) {
|
|
if (depth == 0 || moves->movesNumber == 0)
|
|
return 0;
|
|
currentNode->numChildMoves = moves->movesNumber;
|
|
currentNode->childMove = new gameTree[maxMoves]();
|
|
//switch stone char
|
|
char ownc = getEnemyChar(enemyc);
|
|
|
|
//#ifdef debugprint
|
|
// printf("Depth %d: enemy: %c\n", depth, enemyc);
|
|
//#endif
|
|
for(size_t i=0; i<moves->movesNumber; ++i)
|
|
{
|
|
currentNode->childMove[i].nodeMove = moves->list[i];
|
|
size_t fieldMemSize = fieldSize*fieldSize*sizeof(cell);
|
|
auto futureField = new cell[fieldSize][fieldSize]();
|
|
memcpy(futureField, field, fieldMemSize);
|
|
futureField[(moves->list[i].turnRow-1)][(moves->list[i].turnCol-1)].content=enemyc;
|
|
movesList *futureMoves = new movesList();
|
|
findMoves(futureField, futureMoves, enemyc);
|
|
buildTree(&(currentNode->childMove[i]), futureField, futureMoves, depth-1, getEnemyChar(enemyc));
|
|
delete [] futureMoves;
|
|
delete [] futureField;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
move
|
|
maxEvalMove(std::vector<evalMove> *curMoves) {
|
|
evalMove maxM;
|
|
maxM.evaluation = -9;
|
|
for(size_t i = 0; i < curMoves->size(); ++i)
|
|
{
|
|
if(curMoves->at(i).evaluation > maxM.evaluation)
|
|
{
|
|
maxM.evaluation = curMoves->at(i).evaluation;
|
|
maxM.m = curMoves->at(i).m;
|
|
}
|
|
}
|
|
return maxM.m;
|
|
}
|
|
|
|
int
|
|
maxInt(std::vector<int> *evaluations) {
|
|
int maxE = -10;
|
|
for(size_t i = 0; i<evaluations->size(); ++i)
|
|
{
|
|
if(evaluations->at(i) > maxE)
|
|
{
|
|
maxE = evaluations->at(i);
|
|
}
|
|
}
|
|
return maxE;
|
|
}
|
|
|
|
//MiniMax Algorithmus
|
|
int
|
|
eval(gameTree *currentNode, int depth) {
|
|
if (depth == 0 || !currentNode->numChildMoves)
|
|
{
|
|
//gut: hohe Mobilität, Eckzüge. Schlecht: Züge auf Felder direkt vor der Ecke
|
|
return (currentNode->numChildMoves + 5*isCornerField(currentNode->nodeMove) - 2*isCOrXField(currentNode->nodeMove));
|
|
}
|
|
|
|
std::vector<int> curEvals;
|
|
for(size_t i = 0; i < currentNode->numChildMoves; ++i)
|
|
{
|
|
int curEval = -1 * eval(&(currentNode->childMove[i]), depth-1);
|
|
curEvals.push_back(curEval);
|
|
}
|
|
return maxInt(&curEvals);
|
|
}
|
|
|
|
move
|
|
findBestMove(gameTree *currentNode, int depth) {
|
|
std::vector<evalMove> curMoves;
|
|
for(size_t i = 0; i < currentNode->numChildMoves; ++i)
|
|
{
|
|
evalMove m;
|
|
m.evaluation = -1 * eval(&(currentNode->childMove[i]), depth-1);
|
|
m.m = currentNode->childMove[i].nodeMove;
|
|
#ifdef debugprint
|
|
printf("findBest %d: %d, %d\n", m.evaluation, m.m.turnRow, m.m.turnCol);
|
|
#endif
|
|
curMoves.push_back(m);
|
|
}
|
|
return maxEvalMove(&curMoves);
|
|
}
|
|
|
|
//move
|
|
//nFindBestMove(cell (*field)[fieldSize], movesList *movesa) {
|
|
// std::vector<evalMove> curMoves;
|
|
|
|
|
|
|
|
int main(void)
|
|
{
|
|
int done = 0;
|
|
srandom(time(NULL));
|
|
|
|
char enemyc=0, ownc=0;
|
|
|
|
while (!done) {
|
|
int turn_row = 0;
|
|
int turn_col = 0;
|
|
/* BEGIN PLAYER-SPECIFIC CODE */
|
|
|
|
// 1. Read state
|
|
char state_buffer[128];
|
|
ssize_t bytes = read(CHILD_IN_FD, state_buffer, sizeof(state_buffer));
|
|
if (bytes != 65) // invalid number of chars
|
|
abort();
|
|
|
|
// state_buffer enthält jetzt 65 Zeichen ('.' oder 'X' oder 'O'):
|
|
// * Das ERSTE Zeichen gibt an, welcher Spieler an der Reihe ist.
|
|
// * Die weiteren 64 Zeichen definieren die Belegung des Feldes.
|
|
// * Die Belegung wird reihenweise gegeben, d.h. die Zeichen
|
|
// 1 bis 8 definieren die erste Zeile des Feldes, Zeichen 9 bis 17
|
|
// geben die zweite Zeile usw.
|
|
// * X und O stehen hierbei für die jeweiligen Spieler. Leere Felder
|
|
// sind durch einen Punkt (.) gekennzeichnet.
|
|
|
|
// 2. TODO: Strategie hier einfügen. Resultat in turn_row und turn_col speichern.
|
|
|
|
//number of stones of X/O
|
|
unsigned int numX=0, numO=0; //TODO: make it a property of field struct
|
|
ownc = state_buffer[0];
|
|
|
|
if(!enemyc)
|
|
{
|
|
enemyc = getEnemyChar(ownc);
|
|
}
|
|
|
|
auto field = new cell[fieldSize][fieldSize]();
|
|
readStateBuffer(state_buffer, field, &numX, &numO); //stateBuffer ist Pointer auf char, field ist Pointer auf struct cell[]
|
|
movesList *moves = new movesList();
|
|
|
|
findMoves(field, moves, enemyc);
|
|
|
|
// unsigned int *numOfEnemyMoves = numNextMoves(field, moves);
|
|
|
|
// for(size_t i=0; i<moves->movesNumber; ++i)
|
|
// {
|
|
// printf("Moves %d %d -> %d enemy moves\n", moves->list[i].turnRow, moves->list[i].turnCol, numOfEnemyMoves[i]);
|
|
// }
|
|
|
|
if(!(moves->movesNumber))
|
|
{
|
|
turn_row=0;
|
|
turn_col=0;
|
|
#ifdef debugprint
|
|
printf("Nope\n");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
|
|
if((numX+numO)>5*fieldSize*fieldSize/6)
|
|
endgame = 1;
|
|
gameTree *treeRoot = new gameTree();
|
|
treeRoot->nodeMove.turnRow = 0;
|
|
treeRoot->nodeMove.turnCol = 0;
|
|
buildTree(treeRoot, field, moves, 1, getEnemyChar(enemyc));
|
|
move finalMove = findBestMove(treeRoot, 1);
|
|
|
|
turn_row=finalMove.turnRow;
|
|
turn_col=finalMove.turnCol;
|
|
#ifdef debugprint
|
|
printf("Getätigter Zug: %d,%d\n", turn_row, turn_col);
|
|
#endif
|
|
}
|
|
|
|
|
|
// 3. Return result
|
|
send_move(turn_row, turn_col);
|
|
|
|
//free(numOfEnemyMoves);
|
|
delete [] field;
|
|
delete [] moves;
|
|
/* END PLAYER-SPECIFIC CODE */
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* EOF */
|