Othello AI
2008-01-13
/*
* This AI works with a combination of minimizing losses,
* evaluating and pruning a minimax-tree and square-value avaluation.
*
* All features can be tuned with the parameters in the beginning of the file.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "board.h"
#include "game.h"
#include "player.h"
#include "util.h"
//#define float float_t
/*
* Tune these for performance
* INITIAL_GAME_TREE_DEPTH must be >= 2
*/
// -------- Game Tree building --------
// - normal parameters -
#define INITIAL_GAME_TREE_DEPTH 4
#define BRANCHING_FACTOR 3
// Game tree extension:
#define EXTEND_GT_TIMES 2
#define EXTEND_DEPTH 1
#define EXTEND_BRANCHING_FACTOR 2
// - end-game parameters -
#define END_GAME_THRESHOLD 10
#define EG_INITIAL_GAME_TREE_DEPTH 10
#define EG_BRANCHING_FACTOR 3
// Game tree extension:
#define EG_EXTEND_GT_TIMES 0
#define EG_EXTEND_DEPTH 1
#define EG_EXTEND_BRANCHING_FACTOR 2
// -------- Desicion making coefficients --------
// worst case from initial game tree
#define WC_CE 5.0
// Minimax average from extended game tree
#define MM_CE 10.0
// Mobility factor out of two levels of initial game tree
#define MO_CE 1.5
// Same for end-game
#define EG_WC_CE 1.0
#define EG_MM_CE 0.0
// A smaller MOBILITY_COEFFICIENT gives mobility a bigger effect (~4)
#define MOBILITY_COEFFICIENT 4
// -------- Values for different square types --------
#define DEFAULT_SQUARE_VALUE 1
#define EDGE_CENTER_VALUE 2
#define CENTER_VALUE 0.5
#define CORNER_VALUE 35
#define X_SQUARE_VALUE -10
#define C_SQUARE_VALUE -2
#define EDGE_CONTROL_VALUE 5
// because some squares have nagative values, there must an offset to
// avoid false value ratios. e.g. 10/-5 == -10/5
// SCORE_OFFSET makes all scores positive
#define SCORE_OFFSET 50
// maximum value for any board ratio
#define MAX_POINTS 300
/* --------- Game State ------------- */
typedef struct GameState_s {
struct Game *game;
float value;
} *GameState;
/** Construct new gamestate from game **/
GameState gameStateConstruct(struct Game* game);
/** Destruct gameState **/
void gameStateDestruct(GameState state);
/** Free resources used by Game-pointer in gamestate **/
void gameStateFreeGame(GameState state);
/** calculate disc-square table **/
float** getSquareValues(struct Game const* game, int player);
/** destruct disc-square table **/
void squareValuesDestruct(float** values);
/** tell if game is starting to end **/
int isEndGame(struct Game const* game);
/** calculate value for current game state in tree according to disc-square table **/
void gameStateCalculateValue(GameState state, int player);
/** calculate value for current game state in tree according to raw score ratio **/
void gameStateCalculateValueRaw(GameState state, int player);
/* ------------- (Game) Tree: ------------- */
// Data abstraction
typedef GameState Data;
#define dataDestruct gameStateDestruct
typedef struct Tree_s {
Data data;
struct Tree_s* parent;
struct Tree_s** children;
unsigned int num_children; // number of children
unsigned int num_child; // tree is num_child:st child of parent
} *Tree;
/** construct new tree with data in it **/
Tree treeConstruct(Data data);
/** add child to tree **/
void treeAddChild(Tree tree, Tree child);
/** Remove (and destruct) tree **/
void treeRemove(Tree tree);
/** Detach tree from parent **/
void treeDetach(Tree tree);
/** Destruct tree **/
void treeDestruct(Tree tree);
/** get last child at far left **/
Tree treeGetFirstLeaf(Tree tree);
/** get next leaf **/
Tree treeGetNextLeaf(Tree tree);
/** Get first child of tree **/
Tree treeGetFirstChild(Tree tree);
/** get next sibling **/
Tree treeGetNextSibling(Tree tree);
/** return 1 if tree is descendant of ancestor, else 0 **/
int treeIsDescendantOf(Tree tree, Tree ancestor);
/** return 1 if parent has child child, else 0 **/
int treeHasChild(Tree parent, Tree child);
/* ------------- Initial moves ------------------- */
typedef struct InitialMove_s {
int elements; // number of elements
Tree *trees; // trees corrensponding to moves
struct Coord *moves; // coordinates of moves
float *wc_values; // worst-case-values
float *mm_values; // minimax-pruned values
float *mobility_factors;
} *InitialMove;
/** Construct new set of initial moves **/
InitialMove initialMoveConstruct(void);
/** add a new move **/
void initialMoveAdd(InitialMove im, Tree tree, struct Coord move);
/** remove all "dead" trees from initial move -sets **/
void initialMoveUpdate(InitialMove im, Tree root);
/** evaluate worst case values for initial moves **/
void initialMoveEvaluateWC(InitialMove im);
/** evaluate minimax values for initial moves (worst case) **/
void initialMoveEvaluateMM(InitialMove im);
/** evaluate mobility coefficient **/
void initialMoveEvaluateMobility(InitialMove im);
/** Destruct set of moves **/
void initialMoveDestruct(InitialMove im);
/* ----------Game tree buildining & pruning ----------------- **/
/** build a game tree depth high, scores corresponding to player.
* Save initial moves into moves if moves != NULL
**/
void buildGameTree(Tree tree, int depth, int player, InitialMove im, int endgame);
/** Extend game tree. New game trees are built downwars from leaves of tree.
* New trees are depth deep and have branching factor of brancing_factor.
* current_depth is depth in whole game tree (current state depth = 0)
**/
void extendGameTree(Tree tree, int current_depth, int depth, int branching_factor, int player, int endgame);
/** Populate game-tree node with children representing all valid moves.
* Save initial moves into moves if moves != NULL
**/
int treePopulateNode(Tree tree, InitialMove moves);
/** Prune game tree using minimax method **/
void pruneMinimax(Tree tree, unsigned int branching_factor, int invert);
void pruneMin(Tree tree, unsigned int branching_factor);
void pruneMax(Tree tree, unsigned int branching_factor);
/** calculate tree values according to worst case from children (recursive) **/
float treeUpdateValues(Tree root);
/*
* "Main" functions.
*/
/** Get best possible move from inital move structure **/
struct Coord getBestMove(InitialMove im);
/** Get best possible move from inital move structure for end game **/
struct Coord getBestMoveEG(InitialMove im);
/** Play as player, using artificial intelligence. Returns the player's move. **/
struct Coord aiPlay(struct Game const* game) {
struct Coord best_move_coords;
InitialMove im;
int player = gameCurrentPlayer(game);
Data data = gameStateConstruct(gameCopy(game));
Tree root = treeConstruct(data);
im = initialMoveConstruct();
if (!isEndGame(game)) {
// Build & prune initial game tree
buildGameTree(root, INITIAL_GAME_TREE_DEPTH, player, im, 0);
// evaluate worst case and mobility
initialMoveEvaluateWC(im);
initialMoveEvaluateMobility(im);
// prune initial game tree
pruneMinimax(root, BRANCHING_FACTOR, 0);
initialMoveUpdate(im, root);
// Extend while pruning
int depth = INITIAL_GAME_TREE_DEPTH;
for (int i = 0; i < EXTEND_GT_TIMES; i++) {
extendGameTree(root, depth, EXTEND_DEPTH, EXTEND_BRANCHING_FACTOR, player, 0);
depth += EXTEND_DEPTH;
}
// evaluate minimax-value
initialMoveEvaluateMM(im);
best_move_coords = getBestMove(im);
} else {
// Build & prune initial game tree
buildGameTree(root, EG_INITIAL_GAME_TREE_DEPTH, player, im, 1);
// evaluate worst case and mobility
initialMoveEvaluateWC(im);
// prune initial game tree
pruneMinimax(root, EG_BRANCHING_FACTOR, 0);
initialMoveUpdate(im, root);
// Extend while pruning
int depth = INITIAL_GAME_TREE_DEPTH;
for (int i = 0; i < EG_EXTEND_GT_TIMES; i++) {
extendGameTree(root, depth, EG_EXTEND_DEPTH, EG_EXTEND_BRANCHING_FACTOR, player, 1);
depth += EG_EXTEND_DEPTH;
}
// evaluate minimax-value
initialMoveEvaluateMM(im);
best_move_coords = getBestMoveEG(im);
}
// Clean up
initialMoveDestruct(im);
treeDestruct(root);
return best_move_coords;
}
struct Coord getBestMove(InitialMove im) {
float best_value = 0, value;
struct Coord best_move;
for(int i = 0; i < im->elements; i++) {
value = (WC_CE * im->wc_values[i] +
MM_CE * im->wc_values[i] +
MO_CE * im->mobility_factors[i])
/ (WC_CE + MM_CE + MO_CE);
if (value > best_value) {
best_value = value;
best_move = im->moves[i];
}
}
return best_move;
}
struct Coord getBestMoveEG(InitialMove im) {
float best_value = 0, value;
struct Coord best_move;
for(int i = 0; i < im->elements; i++) {
value = (EG_WC_CE * im->wc_values[i] +
EG_MM_CE * im->wc_values[i])
/ (EG_WC_CE + EG_MM_CE);
if (value > best_value) {
best_value = value;
best_move = im->moves[i];
}
}
return best_move;
}
/*
* Game tree pruning and building
*/
void buildGameTree(Tree tree, int depth, int player, InitialMove im, int endgame) {
int game_end = 0;
Tree root = tree;
// first moves
game_end = treePopulateNode(tree, im);
for (int i = 0; i < depth && !game_end; i++) {
tree = treeGetFirstLeaf(root);
do {
game_end = treePopulateNode(tree, NULL);
} while((tree = treeGetNextLeaf(tree)) && treeIsDescendantOf(tree, root));
}
// Calulate values for leaves
tree = treeGetFirstLeaf(root);
do {
if (endgame) {
gameStateCalculateValueRaw(tree->data, player);
} else {
gameStateCalculateValue(tree->data, player);
}
} while((tree = treeGetNextLeaf(tree)) && treeIsDescendantOf(tree, root));
// Calculate in-tree-values for nodes to correspond to worst possible case
treeUpdateValues(root);
}
void extendGameTree(Tree tree, int current_depth, int depth, int branching_factor, int player, int endgame) {
Tree root = tree;
int invert = current_depth % 2;
tree = treeGetFirstLeaf(tree);
do {
buildGameTree(tree, depth, player, NULL, endgame);
treeUpdateValues(tree);
pruneMinimax(tree, branching_factor, invert);
} while((tree = treeGetNextLeaf(tree)) && treeIsDescendantOf(tree, root));
}
int treePopulateNode(Tree tree, InitialMove moves) {
unsigned int x, y;
struct Coord move;
int game_end = 0;
int flips;
if(!gameCurrentPlayer(tree->data->game)) return -1;
// Go through each square
for (x = 0; x < 8; x++) {
for (y = 0; y < 8; y++) {
move = coord(x, y);
if((flips = gameNumFlips(tree->data->game, move)) == 0) {
continue; // Move not valid
}
struct Game *new_game = gameCopy(tree->data->game);
game_end = gamePlayCurrent(new_game, move);
Data data = gameStateConstruct(new_game);
Tree child = treeConstruct(data);
treeAddChild(tree, child);
if (moves) initialMoveAdd(moves, child, move);
// If one player wins, the minimax is worst in any case...
if (game_end) break;
}
if (game_end) break;
}
// Destruct game to save memory...
gameStateFreeGame(tree->data);
return game_end;
}
void pruneMinimax(Tree tree, unsigned int branching_factor, int invert) {
if (tree->num_children == 0) return;
Tree child;
// prune
if(!invert) {
pruneMin(tree, branching_factor);
} else {
pruneMax(tree, branching_factor);
}
// invert 'invert'
invert = (invert + 1) % 2;
child = tree->children[0];
do {
pruneMinimax(child, branching_factor, invert);
} while ((child = treeGetNextSibling(child)));
}
void pruneMin(Tree tree, unsigned int branching_factor) {
if (tree->num_children == 0) return;
Tree child, min_child = NULL;
float min_value, value;
while (tree->num_children > branching_factor) {
min_value = INFINITY;
child = tree->children[0];
do {
value = child->data->value;
if (value < min_value) {
min_value = value;
min_child = child;
} else if( value == min_value) { // Pseudo-random replace
if(((unsigned int)((7 + 2 * tree->num_child) * value)) % 2) {
min_child = child;
}
}
} while ((child = treeGetNextSibling(child)));
treeRemove(min_child);
}
}
void pruneMax(Tree tree, unsigned int branching_factor) {
if (tree->num_children == 0) return;
Tree child, max_child = NULL;
float max_value, value;
while (tree->num_children > branching_factor) {
max_value = 0;
child = tree->children[0];
do {
value = child->data->value;
if (value > max_value) {
max_value = value;
max_child = child;
} else if( value == max_value) { // Pseudo-random replace
if(((unsigned int)((7 + 2 * tree->num_child) * value)) % 2) {
max_child = child;
}
}
} while ((child = treeGetNextSibling(child)));
treeRemove(max_child);
}
}
float treeUpdateValues(Tree root) {
float min_value = INFINITY, child_value;
for (unsigned int i = 0; i < root->num_children; i++) {
child_value = treeUpdateValues(root->children[i]);
if (child_value < min_value) {
min_value = child_value;
}
}
if (min_value < INFINITY) {
root->data->value = min_value;
}
return root->data->value;
}
/* Game state functions:
* struct GameState_s {
* struct Game *game;
* float value;
* };
*/
GameState gameStateConstruct(struct Game* game) {
GameState game_state = (GameState) malloc(sizeof(struct GameState_s));
game_state->game = game;
game_state->value = -1;
return game_state;
}
void gameStateDestruct(GameState state) {
if (state->game != NULL) {
gameDestruct(state->game);
}
free(state);
}
void gameStateFreeGame(GameState state) {
if (state->game != NULL) {
gameDestruct(state->game);
}
state->game = NULL;
}
void gameStateCalculateValue(GameState state, int player) {
unsigned int x, y;
int square_type;
struct Coord coord;
float my_points = 0, opponent_points = 0;
float** my_matrix = getSquareValues(state->game, player);
float** opponent_matrix = getSquareValues(state->game, (player % 2) + 1);
for (x = 0; x < 8; x++) {
for (y = 0; y < 8; y++) {
coord.x = x;
coord.y = y;
square_type = gameSquareType(state->game, coord);
if (square_type == player) {
my_points += my_matrix[x][y];
} else if (square_type != 0) {
opponent_points += opponent_matrix[x][y];
}
}
}
squareValuesDestruct(my_matrix);
squareValuesDestruct(opponent_matrix);
// Apply offset to avoid negative ratios:
my_points += SCORE_OFFSET;
opponent_points += SCORE_OFFSET;
if (opponent_points == 0) { // avoid div-by-zero
state->value = MAX_POINTS;
return;
}
state->value = my_points / opponent_points;
}
void gameStateCalculateValueRaw(GameState state, int player) {
unsigned int x, y;
int square_type;
struct Coord coord;
float my_points = 0, opponent_points = 0;
for (x = 0; x < 8; x++) {
for (y = 0; y < 8; y++) {
coord.x = x;
coord.y = y;
square_type = gameSquareType(state->game, coord);
if (square_type == player) {
my_points++;
} else if (square_type != 0) {
opponent_points++;
}
}
}
if (opponent_points == 0) { // avoid div-by-zero
state->value = MAX_POINTS;
return;
}
state->value = my_points / opponent_points;
}
float** getSquareValues(struct Game const* game, int player) {
int opponent = (player % 2) + 1;
float **matrix = (float**) malloc(8 * sizeof(float*));
for (int i = 0; i < 8; i++) {
matrix[i] = (float*) malloc(8 * sizeof(float));
}
for(int x = 0; x < 8; x++) {
for(int y = 0; y < 8; y++) {
matrix[x][y] = DEFAULT_SQUARE_VALUE;
}
}
// Give edge centers and center squares values
matrix[0][3] = matrix[0][4] = matrix[7][3] = matrix[7][4] =
matrix[3][0] = matrix[4][0] = matrix[3][7] = matrix[4][7] = EDGE_CENTER_VALUE;
matrix[3][3] = matrix[3][4] = matrix[4][3] = matrix[4][4] = CENTER_VALUE;
// Give corners a big value and 'corner give' locations small value
// Also, if an edge is free of the opponent, give 'edge control'
// squares big value
matrix[0][0] = matrix[0][7] = matrix[7][0] = matrix[7][7] = CORNER_VALUE;
if (gameSquareEmpty(game, coord(0,0))) {
matrix[1][1] = X_SQUARE_VALUE;
matrix[0][1] = matrix[1][0] = C_SQUARE_VALUE;
if (gameSquareType(game, coord(0,1)) != opponent &&
gameSquareType(game, coord(0,2)) != opponent &&
gameSquareType(game, coord(0,3)) != opponent &&
gameSquareType(game, coord(0,4)) != opponent &&
gameSquareType(game, coord(0,5)) != opponent &&
gameSquareType(game, coord(0,6)) != opponent &&
gameSquareType(game, coord(0,7)) != opponent) {
matrix[0][2] = matrix[0][5] = EDGE_CONTROL_VALUE;
}
}
if (gameSquareEmpty(game, coord(0,7))) {
matrix[1][6] = X_SQUARE_VALUE;
matrix[0][6] = matrix[1][7] = C_SQUARE_VALUE;
if (gameSquareType(game, coord(1,7)) != opponent &&
gameSquareType(game, coord(2,7)) != opponent &&
gameSquareType(game, coord(3,7)) != opponent &&
gameSquareType(game, coord(4,7)) != opponent &&
gameSquareType(game, coord(5,7)) != opponent &&
gameSquareType(game, coord(6,7)) != opponent &&
gameSquareType(game, coord(7,7)) != opponent) {
matrix[2][7] = matrix[5][7] = EDGE_CONTROL_VALUE;
}
}
if (gameSquareEmpty(game, coord(7,0))) {
matrix[6][1] = X_SQUARE_VALUE;
matrix[7][1] = matrix[6][0] = C_SQUARE_VALUE;
if (gameSquareType(game, coord(0,0)) != opponent &&
gameSquareType(game, coord(1,0)) != opponent &&
gameSquareType(game, coord(2,0)) != opponent &&
gameSquareType(game, coord(3,0)) != opponent &&
gameSquareType(game, coord(4,0)) != opponent &&
gameSquareType(game, coord(5,0)) != opponent &&
gameSquareType(game, coord(6,0)) != opponent) {
matrix[2][0] = matrix[5][0] = EDGE_CONTROL_VALUE;
}
}
if (gameSquareEmpty(game, coord(7,7))) {
matrix[6][6] = X_SQUARE_VALUE;
matrix[6][7] = matrix[7][6] = C_SQUARE_VALUE;
if (gameSquareType(game, coord(7, 0)) != opponent &&
gameSquareType(game, coord(7, 1)) != opponent &&
gameSquareType(game, coord(7, 2)) != opponent &&
gameSquareType(game, coord(7, 3)) != opponent &&
gameSquareType(game, coord(7, 4)) != opponent &&
gameSquareType(game, coord(7, 5)) != opponent &&
gameSquareType(game, coord(7, 6)) != opponent) {
matrix[7][2] = matrix[7][5] = EDGE_CONTROL_VALUE;
}
}
return matrix;
}
void squareValuesDestruct(float** values) {
for (int i = 0; i < 8; i++) {
free(values[i]);
}
free(values);
}
int isEndGame(struct Game const* game) {
unsigned int empty_squares = 0;
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
if (!gameSquareType(game, coord(x, y))) empty_squares++;
}}
if(empty_squares <= END_GAME_THRESHOLD) return 1;
return 0;
}
/* Tree functions
* struct Tree_s {
* Data data;
* struct Tree_s* parent;
* struct Tree_s** children;
* unsigned int num_children;
* unsigned int num_child;
* }
*/
Tree treeConstruct(Data data) {
Tree tree = (Tree) malloc(sizeof(struct Tree_s));
tree->data = data;
tree->parent = NULL;
tree->children = (Tree*) malloc(sizeof(Tree));
tree->children[0] = NULL;
tree->num_children = 0;
tree->num_child = 0;
return tree;
}
void treeRemove(Tree tree) {
treeDetach(tree); // Detach from parent
treeDestruct(tree); // Recursive destruct
}
void treeDestruct(Tree tree) {
// Destruct children
for (unsigned int i = 0; i < tree->num_children; i++) {
treeDestruct(tree->children[i]);
}
// Ddestruct self
dataDestruct(tree->data);
free(tree->children);
free(tree);
}
void treeAddChild(Tree tree, Tree child) {
tree->num_children++;
tree->children = (Tree*) realloc(tree->children, tree->num_children * sizeof(Tree));
tree->children[tree->num_children - 1] = child;
child->parent = tree;
child->num_child = tree->num_children;
}
void treeDetach(Tree tree) {
Tree parent = tree->parent;
if(!parent) return;
// Move and update num_child for siblings of tree
for(unsigned int i = tree->num_child - 1; i < (parent->num_children - 1); i++) {
parent->children[i] = parent->children[i + 1];
parent->children[i]->num_child = i + 1;
}
parent->num_children--;
parent->children = (Tree*) realloc(parent->children, parent->num_children * sizeof(Tree));
}
Tree treeGetFirstLeaf(Tree tree) {
if(tree->num_children == 0) return tree;
if(tree == NULL) return NULL;
while(tree->num_children > 0) {
tree = tree->children[0];
}
return tree;
}
Tree treeGetNextSibling(Tree tree) {
if (tree->parent == NULL) return NULL; // Special case...
if (tree->parent->num_children <= tree->num_child) return NULL;
return tree->parent->children[tree->num_child];
}
Tree treeGetNextLeaf(Tree tree) {
Tree temp;
// Get next sibling if applicable
if ((temp = treeGetNextSibling(tree))) return temp;
// Go up in tree as long as node is the last child of its parent
while(tree->parent && !treeGetNextSibling(tree)) {
tree = tree->parent;
}
// Get next sibling or return null if end has been reached
if (!(tree = treeGetNextSibling(tree))) return NULL;
// Go back to bottom level
return treeGetFirstLeaf(tree);
}
Tree treeGetFirstChild(Tree tree) {
if (tree->num_children == 0) return NULL;
return tree->children[0];
}
int treeIsDescendantOf(Tree tree, Tree ancestor) {
if (tree == NULL) return 0;
while ((tree = tree->parent)) {
if (tree == ancestor) return 1;
}
return 0;
}
int treeHasChild(Tree parent, Tree child) {
Tree test;
for(test = treeGetFirstChild(parent); test; test = treeGetNextSibling(test)) {
if (test == child) return 1;
}
return 0;
}
/* Initial moves
* typedef struct InitialMove_s {
* int elements;
* Tree *trees;
* float *values;
* float *mobility_factors;
* float *mm_values;
* struct Coord *moves;
* } *InitialMove;
*/
InitialMove initialMoveConstruct(void) {
InitialMove im = (InitialMove) malloc(sizeof(struct InitialMove_s));
im->elements = 0;
im->trees = NULL;
im->moves = NULL;
im->wc_values = NULL;
im->mm_values = NULL;
im->mobility_factors = NULL;
return im;
}
void initialMoveDestruct(InitialMove im) {
free(im->trees);
free(im->moves);
free(im->wc_values);
free(im->mm_values);
free(im->mobility_factors);
free(im);
}
void initialMoveAdd(InitialMove im, Tree tree, struct Coord move) {
im->trees = (Tree*) realloc(im->trees, (im->elements + 1) * sizeof(Tree));
im->trees[im->elements] = tree;
im->wc_values = (float*) realloc(im->wc_values, (im->elements + 1) * sizeof(float));
im->mm_values = (float*) realloc(im->mm_values, (im->elements + 1) * sizeof(float));
im->mobility_factors = (float*) realloc(im->mobility_factors, (im->elements + 1) * sizeof(float));
im->moves = (struct Coord*) realloc(im->moves, (im->elements + 1) * sizeof(struct Coord));
im->moves[im->elements] = move;
im->elements++;
}
void initialMoveEvaluateWC(InitialMove im) {
for (int i = 0; i < im->elements; i++) {
im->wc_values[i] = im->trees[i]->data->value;
}
}
void initialMoveEvaluateMM(InitialMove im) {
for (int i = 0; i < im->elements; i++) {
im->mm_values[i] = im->trees[i]->data->value;
}
}
void initialMoveEvaluateMobility(InitialMove im) {
Tree tree;
unsigned int opponent_mobility, my_mobility = (unsigned int) INFINITY;
float mobility_coefficient, mc = MOBILITY_COEFFICIENT;
for (int i = 0; i < im->elements; i++) {
tree = im->trees[i];
if(!tree || !(tree->num_children)) return;
// get own mobility
opponent_mobility = tree->num_children;
// Get worst case own mobility after opponent moves
for(unsigned int j = 0; j < tree->num_children; j++) {
if (tree->children[j]->num_children < my_mobility) {
my_mobility = tree->children[j]->num_children;
}
}
mobility_coefficient = (float) my_mobility / (float) opponent_mobility;
mobility_coefficient = (mc + mobility_coefficient) / (1.2 * mc);
im->mobility_factors[i] = mobility_coefficient;
}
}
void initialMoveUpdate(InitialMove im, Tree root) {
for (int i = 0; i < im->elements; i++) {
if (treeHasChild(root, im->trees[i])) continue;
/* Tree has been removed -> remove corrensponding move */
// move remaining elements backwards
for (int index = i + 1; index < im->elements; index++) {
im->trees[index - 1] = im->trees[index];
im->moves[index - 1] = im->moves[index];
im->wc_values[index - 1] = im->wc_values[index];
im->mm_values[index - 1] = im->mm_values[index];
im->mobility_factors[index - 1] = im->mobility_factors[index];
}
i--; // otherwise next tree wont be evaluated...
im->elements--;
// Realloc all members of InitialMove_s
im->trees = (Tree*) realloc(im->trees, im->elements * sizeof(Tree));
im->moves = (struct Coord*) realloc(im->moves, im->elements * sizeof(struct Coord));
im->wc_values = (float*) realloc(im->wc_values, im->elements * sizeof(float));
im->mm_values = (float*) realloc(im->mm_values, im->elements * sizeof(float));
im->mobility_factors = (float*) realloc(im->mobility_factors, im->elements * sizeof(float));
}
}
* This AI works with a combination of minimizing losses,
* evaluating and pruning a minimax-tree and square-value avaluation.
*
* All features can be tuned with the parameters in the beginning of the file.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "board.h"
#include "game.h"
#include "player.h"
#include "util.h"
//#define float float_t
/*
* Tune these for performance
* INITIAL_GAME_TREE_DEPTH must be >= 2
*/
// -------- Game Tree building --------
// - normal parameters -
#define INITIAL_GAME_TREE_DEPTH 4
#define BRANCHING_FACTOR 3
// Game tree extension:
#define EXTEND_GT_TIMES 2
#define EXTEND_DEPTH 1
#define EXTEND_BRANCHING_FACTOR 2
// - end-game parameters -
#define END_GAME_THRESHOLD 10
#define EG_INITIAL_GAME_TREE_DEPTH 10
#define EG_BRANCHING_FACTOR 3
// Game tree extension:
#define EG_EXTEND_GT_TIMES 0
#define EG_EXTEND_DEPTH 1
#define EG_EXTEND_BRANCHING_FACTOR 2
// -------- Desicion making coefficients --------
// worst case from initial game tree
#define WC_CE 5.0
// Minimax average from extended game tree
#define MM_CE 10.0
// Mobility factor out of two levels of initial game tree
#define MO_CE 1.5
// Same for end-game
#define EG_WC_CE 1.0
#define EG_MM_CE 0.0
// A smaller MOBILITY_COEFFICIENT gives mobility a bigger effect (~4)
#define MOBILITY_COEFFICIENT 4
// -------- Values for different square types --------
#define DEFAULT_SQUARE_VALUE 1
#define EDGE_CENTER_VALUE 2
#define CENTER_VALUE 0.5
#define CORNER_VALUE 35
#define X_SQUARE_VALUE -10
#define C_SQUARE_VALUE -2
#define EDGE_CONTROL_VALUE 5
// because some squares have nagative values, there must an offset to
// avoid false value ratios. e.g. 10/-5 == -10/5
// SCORE_OFFSET makes all scores positive
#define SCORE_OFFSET 50
// maximum value for any board ratio
#define MAX_POINTS 300
/* --------- Game State ------------- */
typedef struct GameState_s {
struct Game *game;
float value;
} *GameState;
/** Construct new gamestate from game **/
GameState gameStateConstruct(struct Game* game);
/** Destruct gameState **/
void gameStateDestruct(GameState state);
/** Free resources used by Game-pointer in gamestate **/
void gameStateFreeGame(GameState state);
/** calculate disc-square table **/
float** getSquareValues(struct Game const* game, int player);
/** destruct disc-square table **/
void squareValuesDestruct(float** values);
/** tell if game is starting to end **/
int isEndGame(struct Game const* game);
/** calculate value for current game state in tree according to disc-square table **/
void gameStateCalculateValue(GameState state, int player);
/** calculate value for current game state in tree according to raw score ratio **/
void gameStateCalculateValueRaw(GameState state, int player);
/* ------------- (Game) Tree: ------------- */
// Data abstraction
typedef GameState Data;
#define dataDestruct gameStateDestruct
typedef struct Tree_s {
Data data;
struct Tree_s* parent;
struct Tree_s** children;
unsigned int num_children; // number of children
unsigned int num_child; // tree is num_child:st child of parent
} *Tree;
/** construct new tree with data in it **/
Tree treeConstruct(Data data);
/** add child to tree **/
void treeAddChild(Tree tree, Tree child);
/** Remove (and destruct) tree **/
void treeRemove(Tree tree);
/** Detach tree from parent **/
void treeDetach(Tree tree);
/** Destruct tree **/
void treeDestruct(Tree tree);
/** get last child at far left **/
Tree treeGetFirstLeaf(Tree tree);
/** get next leaf **/
Tree treeGetNextLeaf(Tree tree);
/** Get first child of tree **/
Tree treeGetFirstChild(Tree tree);
/** get next sibling **/
Tree treeGetNextSibling(Tree tree);
/** return 1 if tree is descendant of ancestor, else 0 **/
int treeIsDescendantOf(Tree tree, Tree ancestor);
/** return 1 if parent has child child, else 0 **/
int treeHasChild(Tree parent, Tree child);
/* ------------- Initial moves ------------------- */
typedef struct InitialMove_s {
int elements; // number of elements
Tree *trees; // trees corrensponding to moves
struct Coord *moves; // coordinates of moves
float *wc_values; // worst-case-values
float *mm_values; // minimax-pruned values
float *mobility_factors;
} *InitialMove;
/** Construct new set of initial moves **/
InitialMove initialMoveConstruct(void);
/** add a new move **/
void initialMoveAdd(InitialMove im, Tree tree, struct Coord move);
/** remove all "dead" trees from initial move -sets **/
void initialMoveUpdate(InitialMove im, Tree root);
/** evaluate worst case values for initial moves **/
void initialMoveEvaluateWC(InitialMove im);
/** evaluate minimax values for initial moves (worst case) **/
void initialMoveEvaluateMM(InitialMove im);
/** evaluate mobility coefficient **/
void initialMoveEvaluateMobility(InitialMove im);
/** Destruct set of moves **/
void initialMoveDestruct(InitialMove im);
/* ----------Game tree buildining & pruning ----------------- **/
/** build a game tree depth high, scores corresponding to player.
* Save initial moves into moves if moves != NULL
**/
void buildGameTree(Tree tree, int depth, int player, InitialMove im, int endgame);
/** Extend game tree. New game trees are built downwars from leaves of tree.
* New trees are depth deep and have branching factor of brancing_factor.
* current_depth is depth in whole game tree (current state depth = 0)
**/
void extendGameTree(Tree tree, int current_depth, int depth, int branching_factor, int player, int endgame);
/** Populate game-tree node with children representing all valid moves.
* Save initial moves into moves if moves != NULL
**/
int treePopulateNode(Tree tree, InitialMove moves);
/** Prune game tree using minimax method **/
void pruneMinimax(Tree tree, unsigned int branching_factor, int invert);
void pruneMin(Tree tree, unsigned int branching_factor);
void pruneMax(Tree tree, unsigned int branching_factor);
/** calculate tree values according to worst case from children (recursive) **/
float treeUpdateValues(Tree root);
/*
* "Main" functions.
*/
/** Get best possible move from inital move structure **/
struct Coord getBestMove(InitialMove im);
/** Get best possible move from inital move structure for end game **/
struct Coord getBestMoveEG(InitialMove im);
/** Play as player, using artificial intelligence. Returns the player's move. **/
struct Coord aiPlay(struct Game const* game) {
struct Coord best_move_coords;
InitialMove im;
int player = gameCurrentPlayer(game);
Data data = gameStateConstruct(gameCopy(game));
Tree root = treeConstruct(data);
im = initialMoveConstruct();
if (!isEndGame(game)) {
// Build & prune initial game tree
buildGameTree(root, INITIAL_GAME_TREE_DEPTH, player, im, 0);
// evaluate worst case and mobility
initialMoveEvaluateWC(im);
initialMoveEvaluateMobility(im);
// prune initial game tree
pruneMinimax(root, BRANCHING_FACTOR, 0);
initialMoveUpdate(im, root);
// Extend while pruning
int depth = INITIAL_GAME_TREE_DEPTH;
for (int i = 0; i < EXTEND_GT_TIMES; i++) {
extendGameTree(root, depth, EXTEND_DEPTH, EXTEND_BRANCHING_FACTOR, player, 0);
depth += EXTEND_DEPTH;
}
// evaluate minimax-value
initialMoveEvaluateMM(im);
best_move_coords = getBestMove(im);
} else {
// Build & prune initial game tree
buildGameTree(root, EG_INITIAL_GAME_TREE_DEPTH, player, im, 1);
// evaluate worst case and mobility
initialMoveEvaluateWC(im);
// prune initial game tree
pruneMinimax(root, EG_BRANCHING_FACTOR, 0);
initialMoveUpdate(im, root);
// Extend while pruning
int depth = INITIAL_GAME_TREE_DEPTH;
for (int i = 0; i < EG_EXTEND_GT_TIMES; i++) {
extendGameTree(root, depth, EG_EXTEND_DEPTH, EG_EXTEND_BRANCHING_FACTOR, player, 1);
depth += EG_EXTEND_DEPTH;
}
// evaluate minimax-value
initialMoveEvaluateMM(im);
best_move_coords = getBestMoveEG(im);
}
// Clean up
initialMoveDestruct(im);
treeDestruct(root);
return best_move_coords;
}
struct Coord getBestMove(InitialMove im) {
float best_value = 0, value;
struct Coord best_move;
for(int i = 0; i < im->elements; i++) {
value = (WC_CE * im->wc_values[i] +
MM_CE * im->wc_values[i] +
MO_CE * im->mobility_factors[i])
/ (WC_CE + MM_CE + MO_CE);
if (value > best_value) {
best_value = value;
best_move = im->moves[i];
}
}
return best_move;
}
struct Coord getBestMoveEG(InitialMove im) {
float best_value = 0, value;
struct Coord best_move;
for(int i = 0; i < im->elements; i++) {
value = (EG_WC_CE * im->wc_values[i] +
EG_MM_CE * im->wc_values[i])
/ (EG_WC_CE + EG_MM_CE);
if (value > best_value) {
best_value = value;
best_move = im->moves[i];
}
}
return best_move;
}
/*
* Game tree pruning and building
*/
void buildGameTree(Tree tree, int depth, int player, InitialMove im, int endgame) {
int game_end = 0;
Tree root = tree;
// first moves
game_end = treePopulateNode(tree, im);
for (int i = 0; i < depth && !game_end; i++) {
tree = treeGetFirstLeaf(root);
do {
game_end = treePopulateNode(tree, NULL);
} while((tree = treeGetNextLeaf(tree)) && treeIsDescendantOf(tree, root));
}
// Calulate values for leaves
tree = treeGetFirstLeaf(root);
do {
if (endgame) {
gameStateCalculateValueRaw(tree->data, player);
} else {
gameStateCalculateValue(tree->data, player);
}
} while((tree = treeGetNextLeaf(tree)) && treeIsDescendantOf(tree, root));
// Calculate in-tree-values for nodes to correspond to worst possible case
treeUpdateValues(root);
}
void extendGameTree(Tree tree, int current_depth, int depth, int branching_factor, int player, int endgame) {
Tree root = tree;
int invert = current_depth % 2;
tree = treeGetFirstLeaf(tree);
do {
buildGameTree(tree, depth, player, NULL, endgame);
treeUpdateValues(tree);
pruneMinimax(tree, branching_factor, invert);
} while((tree = treeGetNextLeaf(tree)) && treeIsDescendantOf(tree, root));
}
int treePopulateNode(Tree tree, InitialMove moves) {
unsigned int x, y;
struct Coord move;
int game_end = 0;
int flips;
if(!gameCurrentPlayer(tree->data->game)) return -1;
// Go through each square
for (x = 0; x < 8; x++) {
for (y = 0; y < 8; y++) {
move = coord(x, y);
if((flips = gameNumFlips(tree->data->game, move)) == 0) {
continue; // Move not valid
}
struct Game *new_game = gameCopy(tree->data->game);
game_end = gamePlayCurrent(new_game, move);
Data data = gameStateConstruct(new_game);
Tree child = treeConstruct(data);
treeAddChild(tree, child);
if (moves) initialMoveAdd(moves, child, move);
// If one player wins, the minimax is worst in any case...
if (game_end) break;
}
if (game_end) break;
}
// Destruct game to save memory...
gameStateFreeGame(tree->data);
return game_end;
}
void pruneMinimax(Tree tree, unsigned int branching_factor, int invert) {
if (tree->num_children == 0) return;
Tree child;
// prune
if(!invert) {
pruneMin(tree, branching_factor);
} else {
pruneMax(tree, branching_factor);
}
// invert 'invert'
invert = (invert + 1) % 2;
child = tree->children[0];
do {
pruneMinimax(child, branching_factor, invert);
} while ((child = treeGetNextSibling(child)));
}
void pruneMin(Tree tree, unsigned int branching_factor) {
if (tree->num_children == 0) return;
Tree child, min_child = NULL;
float min_value, value;
while (tree->num_children > branching_factor) {
min_value = INFINITY;
child = tree->children[0];
do {
value = child->data->value;
if (value < min_value) {
min_value = value;
min_child = child;
} else if( value == min_value) { // Pseudo-random replace
if(((unsigned int)((7 + 2 * tree->num_child) * value)) % 2) {
min_child = child;
}
}
} while ((child = treeGetNextSibling(child)));
treeRemove(min_child);
}
}
void pruneMax(Tree tree, unsigned int branching_factor) {
if (tree->num_children == 0) return;
Tree child, max_child = NULL;
float max_value, value;
while (tree->num_children > branching_factor) {
max_value = 0;
child = tree->children[0];
do {
value = child->data->value;
if (value > max_value) {
max_value = value;
max_child = child;
} else if( value == max_value) { // Pseudo-random replace
if(((unsigned int)((7 + 2 * tree->num_child) * value)) % 2) {
max_child = child;
}
}
} while ((child = treeGetNextSibling(child)));
treeRemove(max_child);
}
}
float treeUpdateValues(Tree root) {
float min_value = INFINITY, child_value;
for (unsigned int i = 0; i < root->num_children; i++) {
child_value = treeUpdateValues(root->children[i]);
if (child_value < min_value) {
min_value = child_value;
}
}
if (min_value < INFINITY) {
root->data->value = min_value;
}
return root->data->value;
}
/* Game state functions:
* struct GameState_s {
* struct Game *game;
* float value;
* };
*/
GameState gameStateConstruct(struct Game* game) {
GameState game_state = (GameState) malloc(sizeof(struct GameState_s));
game_state->game = game;
game_state->value = -1;
return game_state;
}
void gameStateDestruct(GameState state) {
if (state->game != NULL) {
gameDestruct(state->game);
}
free(state);
}
void gameStateFreeGame(GameState state) {
if (state->game != NULL) {
gameDestruct(state->game);
}
state->game = NULL;
}
void gameStateCalculateValue(GameState state, int player) {
unsigned int x, y;
int square_type;
struct Coord coord;
float my_points = 0, opponent_points = 0;
float** my_matrix = getSquareValues(state->game, player);
float** opponent_matrix = getSquareValues(state->game, (player % 2) + 1);
for (x = 0; x < 8; x++) {
for (y = 0; y < 8; y++) {
coord.x = x;
coord.y = y;
square_type = gameSquareType(state->game, coord);
if (square_type == player) {
my_points += my_matrix[x][y];
} else if (square_type != 0) {
opponent_points += opponent_matrix[x][y];
}
}
}
squareValuesDestruct(my_matrix);
squareValuesDestruct(opponent_matrix);
// Apply offset to avoid negative ratios:
my_points += SCORE_OFFSET;
opponent_points += SCORE_OFFSET;
if (opponent_points == 0) { // avoid div-by-zero
state->value = MAX_POINTS;
return;
}
state->value = my_points / opponent_points;
}
void gameStateCalculateValueRaw(GameState state, int player) {
unsigned int x, y;
int square_type;
struct Coord coord;
float my_points = 0, opponent_points = 0;
for (x = 0; x < 8; x++) {
for (y = 0; y < 8; y++) {
coord.x = x;
coord.y = y;
square_type = gameSquareType(state->game, coord);
if (square_type == player) {
my_points++;
} else if (square_type != 0) {
opponent_points++;
}
}
}
if (opponent_points == 0) { // avoid div-by-zero
state->value = MAX_POINTS;
return;
}
state->value = my_points / opponent_points;
}
float** getSquareValues(struct Game const* game, int player) {
int opponent = (player % 2) + 1;
float **matrix = (float**) malloc(8 * sizeof(float*));
for (int i = 0; i < 8; i++) {
matrix[i] = (float*) malloc(8 * sizeof(float));
}
for(int x = 0; x < 8; x++) {
for(int y = 0; y < 8; y++) {
matrix[x][y] = DEFAULT_SQUARE_VALUE;
}
}
// Give edge centers and center squares values
matrix[0][3] = matrix[0][4] = matrix[7][3] = matrix[7][4] =
matrix[3][0] = matrix[4][0] = matrix[3][7] = matrix[4][7] = EDGE_CENTER_VALUE;
matrix[3][3] = matrix[3][4] = matrix[4][3] = matrix[4][4] = CENTER_VALUE;
// Give corners a big value and 'corner give' locations small value
// Also, if an edge is free of the opponent, give 'edge control'
// squares big value
matrix[0][0] = matrix[0][7] = matrix[7][0] = matrix[7][7] = CORNER_VALUE;
if (gameSquareEmpty(game, coord(0,0))) {
matrix[1][1] = X_SQUARE_VALUE;
matrix[0][1] = matrix[1][0] = C_SQUARE_VALUE;
if (gameSquareType(game, coord(0,1)) != opponent &&
gameSquareType(game, coord(0,2)) != opponent &&
gameSquareType(game, coord(0,3)) != opponent &&
gameSquareType(game, coord(0,4)) != opponent &&
gameSquareType(game, coord(0,5)) != opponent &&
gameSquareType(game, coord(0,6)) != opponent &&
gameSquareType(game, coord(0,7)) != opponent) {
matrix[0][2] = matrix[0][5] = EDGE_CONTROL_VALUE;
}
}
if (gameSquareEmpty(game, coord(0,7))) {
matrix[1][6] = X_SQUARE_VALUE;
matrix[0][6] = matrix[1][7] = C_SQUARE_VALUE;
if (gameSquareType(game, coord(1,7)) != opponent &&
gameSquareType(game, coord(2,7)) != opponent &&
gameSquareType(game, coord(3,7)) != opponent &&
gameSquareType(game, coord(4,7)) != opponent &&
gameSquareType(game, coord(5,7)) != opponent &&
gameSquareType(game, coord(6,7)) != opponent &&
gameSquareType(game, coord(7,7)) != opponent) {
matrix[2][7] = matrix[5][7] = EDGE_CONTROL_VALUE;
}
}
if (gameSquareEmpty(game, coord(7,0))) {
matrix[6][1] = X_SQUARE_VALUE;
matrix[7][1] = matrix[6][0] = C_SQUARE_VALUE;
if (gameSquareType(game, coord(0,0)) != opponent &&
gameSquareType(game, coord(1,0)) != opponent &&
gameSquareType(game, coord(2,0)) != opponent &&
gameSquareType(game, coord(3,0)) != opponent &&
gameSquareType(game, coord(4,0)) != opponent &&
gameSquareType(game, coord(5,0)) != opponent &&
gameSquareType(game, coord(6,0)) != opponent) {
matrix[2][0] = matrix[5][0] = EDGE_CONTROL_VALUE;
}
}
if (gameSquareEmpty(game, coord(7,7))) {
matrix[6][6] = X_SQUARE_VALUE;
matrix[6][7] = matrix[7][6] = C_SQUARE_VALUE;
if (gameSquareType(game, coord(7, 0)) != opponent &&
gameSquareType(game, coord(7, 1)) != opponent &&
gameSquareType(game, coord(7, 2)) != opponent &&
gameSquareType(game, coord(7, 3)) != opponent &&
gameSquareType(game, coord(7, 4)) != opponent &&
gameSquareType(game, coord(7, 5)) != opponent &&
gameSquareType(game, coord(7, 6)) != opponent) {
matrix[7][2] = matrix[7][5] = EDGE_CONTROL_VALUE;
}
}
return matrix;
}
void squareValuesDestruct(float** values) {
for (int i = 0; i < 8; i++) {
free(values[i]);
}
free(values);
}
int isEndGame(struct Game const* game) {
unsigned int empty_squares = 0;
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
if (!gameSquareType(game, coord(x, y))) empty_squares++;
}}
if(empty_squares <= END_GAME_THRESHOLD) return 1;
return 0;
}
/* Tree functions
* struct Tree_s {
* Data data;
* struct Tree_s* parent;
* struct Tree_s** children;
* unsigned int num_children;
* unsigned int num_child;
* }
*/
Tree treeConstruct(Data data) {
Tree tree = (Tree) malloc(sizeof(struct Tree_s));
tree->data = data;
tree->parent = NULL;
tree->children = (Tree*) malloc(sizeof(Tree));
tree->children[0] = NULL;
tree->num_children = 0;
tree->num_child = 0;
return tree;
}
void treeRemove(Tree tree) {
treeDetach(tree); // Detach from parent
treeDestruct(tree); // Recursive destruct
}
void treeDestruct(Tree tree) {
// Destruct children
for (unsigned int i = 0; i < tree->num_children; i++) {
treeDestruct(tree->children[i]);
}
// Ddestruct self
dataDestruct(tree->data);
free(tree->children);
free(tree);
}
void treeAddChild(Tree tree, Tree child) {
tree->num_children++;
tree->children = (Tree*) realloc(tree->children, tree->num_children * sizeof(Tree));
tree->children[tree->num_children - 1] = child;
child->parent = tree;
child->num_child = tree->num_children;
}
void treeDetach(Tree tree) {
Tree parent = tree->parent;
if(!parent) return;
// Move and update num_child for siblings of tree
for(unsigned int i = tree->num_child - 1; i < (parent->num_children - 1); i++) {
parent->children[i] = parent->children[i + 1];
parent->children[i]->num_child = i + 1;
}
parent->num_children--;
parent->children = (Tree*) realloc(parent->children, parent->num_children * sizeof(Tree));
}
Tree treeGetFirstLeaf(Tree tree) {
if(tree->num_children == 0) return tree;
if(tree == NULL) return NULL;
while(tree->num_children > 0) {
tree = tree->children[0];
}
return tree;
}
Tree treeGetNextSibling(Tree tree) {
if (tree->parent == NULL) return NULL; // Special case...
if (tree->parent->num_children <= tree->num_child) return NULL;
return tree->parent->children[tree->num_child];
}
Tree treeGetNextLeaf(Tree tree) {
Tree temp;
// Get next sibling if applicable
if ((temp = treeGetNextSibling(tree))) return temp;
// Go up in tree as long as node is the last child of its parent
while(tree->parent && !treeGetNextSibling(tree)) {
tree = tree->parent;
}
// Get next sibling or return null if end has been reached
if (!(tree = treeGetNextSibling(tree))) return NULL;
// Go back to bottom level
return treeGetFirstLeaf(tree);
}
Tree treeGetFirstChild(Tree tree) {
if (tree->num_children == 0) return NULL;
return tree->children[0];
}
int treeIsDescendantOf(Tree tree, Tree ancestor) {
if (tree == NULL) return 0;
while ((tree = tree->parent)) {
if (tree == ancestor) return 1;
}
return 0;
}
int treeHasChild(Tree parent, Tree child) {
Tree test;
for(test = treeGetFirstChild(parent); test; test = treeGetNextSibling(test)) {
if (test == child) return 1;
}
return 0;
}
/* Initial moves
* typedef struct InitialMove_s {
* int elements;
* Tree *trees;
* float *values;
* float *mobility_factors;
* float *mm_values;
* struct Coord *moves;
* } *InitialMove;
*/
InitialMove initialMoveConstruct(void) {
InitialMove im = (InitialMove) malloc(sizeof(struct InitialMove_s));
im->elements = 0;
im->trees = NULL;
im->moves = NULL;
im->wc_values = NULL;
im->mm_values = NULL;
im->mobility_factors = NULL;
return im;
}
void initialMoveDestruct(InitialMove im) {
free(im->trees);
free(im->moves);
free(im->wc_values);
free(im->mm_values);
free(im->mobility_factors);
free(im);
}
void initialMoveAdd(InitialMove im, Tree tree, struct Coord move) {
im->trees = (Tree*) realloc(im->trees, (im->elements + 1) * sizeof(Tree));
im->trees[im->elements] = tree;
im->wc_values = (float*) realloc(im->wc_values, (im->elements + 1) * sizeof(float));
im->mm_values = (float*) realloc(im->mm_values, (im->elements + 1) * sizeof(float));
im->mobility_factors = (float*) realloc(im->mobility_factors, (im->elements + 1) * sizeof(float));
im->moves = (struct Coord*) realloc(im->moves, (im->elements + 1) * sizeof(struct Coord));
im->moves[im->elements] = move;
im->elements++;
}
void initialMoveEvaluateWC(InitialMove im) {
for (int i = 0; i < im->elements; i++) {
im->wc_values[i] = im->trees[i]->data->value;
}
}
void initialMoveEvaluateMM(InitialMove im) {
for (int i = 0; i < im->elements; i++) {
im->mm_values[i] = im->trees[i]->data->value;
}
}
void initialMoveEvaluateMobility(InitialMove im) {
Tree tree;
unsigned int opponent_mobility, my_mobility = (unsigned int) INFINITY;
float mobility_coefficient, mc = MOBILITY_COEFFICIENT;
for (int i = 0; i < im->elements; i++) {
tree = im->trees[i];
if(!tree || !(tree->num_children)) return;
// get own mobility
opponent_mobility = tree->num_children;
// Get worst case own mobility after opponent moves
for(unsigned int j = 0; j < tree->num_children; j++) {
if (tree->children[j]->num_children < my_mobility) {
my_mobility = tree->children[j]->num_children;
}
}
mobility_coefficient = (float) my_mobility / (float) opponent_mobility;
mobility_coefficient = (mc + mobility_coefficient) / (1.2 * mc);
im->mobility_factors[i] = mobility_coefficient;
}
}
void initialMoveUpdate(InitialMove im, Tree root) {
for (int i = 0; i < im->elements; i++) {
if (treeHasChild(root, im->trees[i])) continue;
/* Tree has been removed -> remove corrensponding move */
// move remaining elements backwards
for (int index = i + 1; index < im->elements; index++) {
im->trees[index - 1] = im->trees[index];
im->moves[index - 1] = im->moves[index];
im->wc_values[index - 1] = im->wc_values[index];
im->mm_values[index - 1] = im->mm_values[index];
im->mobility_factors[index - 1] = im->mobility_factors[index];
}
i--; // otherwise next tree wont be evaluated...
im->elements--;
// Realloc all members of InitialMove_s
im->trees = (Tree*) realloc(im->trees, im->elements * sizeof(Tree));
im->moves = (struct Coord*) realloc(im->moves, im->elements * sizeof(struct Coord));
im->wc_values = (float*) realloc(im->wc_values, im->elements * sizeof(float));
im->mm_values = (float*) realloc(im->mm_values, im->elements * sizeof(float));
im->mobility_factors = (float*) realloc(im->mobility_factors, im->elements * sizeof(float));
}
}