(1) A list of conditionals, like "if you can win, do it; if you can block the opponent from winning, do it; if you can create a double threat for victory, do it", etc.
(2) Minimax search (with alpha-beta pruning and perhaps other improvements, like move-ordering heuristics, transposition tables and iterative deepening).
(3) Monte Carlo Tree Search (again with some improvements, like RAVE and so-called heavy playouts).
I don't recommend (1) if you want a strong agent. (2) is the standard approach for this game and it's the basis of every chess program out there, so there is a lot of good information available about it. (3) is a more recent development, which has been extremely successful in computer go, and can be applied to a class of games much larger than minimax (like games with randomness, games with simultaneous decisions and games with more than two players).
I recommend attempting (2). You can use a random number between -1 and 1 as a first attempt at an evaluation function. If the search is good enough, that's already a very tough player to beat. Once you get to that point, it would make sense to ask the question you are asking, and then you can re-read the answers above.
Here's some pseudocode for a version of minimax called NegaMax that is how I think you should start. In NegaMax, the convention is that the score returned is always from the point of view of the player that is about to move.
double NegaMax(Position &position, int depth_left) {
if (position.game_over())
return position.final_score(); // final_score from the point of view of the player to move, of course.
if (depth_left <= 0 && position.no_immediate_threat())
return static_evaluation(position); // static_evaluation also follows the NegaMax sign convention.
Move moves[7];
int n_moves = position.generate_moves(moves);
double best_score = -Infinity;
for (int i = 0; i < n_moves; ++i) {
position.make_move(move[i]);
double score = -NegaMax(position, depth_left - 1); // Notice the minus sign!
position.undo_move(move[i]);
if (score > best_score)
best_score = score;
}
return best_score;
}
You should have a separate function (search_root would be a good name) that has a similar loop over available moves, but this one should return a move, not a score. It can also print some information about the results of the search, implement iterative deepening, etc.See if you can get that working.
At this point I need to see some serious effort on your part before I waste any more time on this.