It is the standard algorithm for this is called Minimax. It basically builds a tree, where the beginning of the game is the root, and then the children represent every possible move X can make on the first turn, then the children of each of those nodes are all the moves O can make in response, etc. Once the entire tree is filled (which is possible for Tic-Tac-Toe, but for games like Chess computers still don't have enough memory), you work your way back up, assuming both players are smart enough to make the best move and arrive at the optimal move. Here is an explanation of Minimax specifically using Tic Tac Toe as an example.
Yes, there are better ways.
It can be considered that how different mirror views of the board would simplify the number of cases. Also, consider pre-storing "interesting" patterns in arrays and then comparing the game state against the data. For example, one series of patterns would be all the ways a player can win in the next move. Note that with the declaration in L[2], there are only two entries in array L, namely L[0] and L[1]. The references you have to L[2], M[2], etc. are errors that should have been caught by the compiler. Consider turning up the warning level. How this is done depends on the compiler. For gcc, it is -Wall.
This counts as a form of artificial intelligence.