Example #1
0
        /*
         * Depth 2 is easy to win
         * Depth 3 is somewhat harder to win
         * Depth 4 is hard to win
         * Depth 5 is unbeatable
         * Depth 6 is smarter and can win in more situations
         */
        protected byte NextMove(GameManager gameManager, byte distanceFromEnd)
        {
            byte move;
            var  choiceTree = new ChoiceTree();
            var  bestChoice = this.Minimax(gameManager, distanceFromEnd);

            // Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(choiceTree));

            if (bestChoice == null)
            {
                move = (byte)gameManager.GetRemainingMoves().First();
            }
            else
            {
                move = bestChoice.Move;
            }

            return(move);
        }
Example #2
0
        private Choice Minimax(GameManager gameManager, byte maxDistanceFromEnd, byte?distanceFromEnd = null, sbyte?alpha = null, sbyte?beta = null, bool maximizingPlayer = true, ChoiceTree choiceTree = null)
        {
            if (distanceFromEnd == null)
            {
                distanceFromEnd = maxDistanceFromEnd;
            }

            sbyte?bestValue = null;
            byte? bestMove  = null;

            var moves       = gameManager.GetRemainingMoves();
            var bestChoices = new List <Choice>();

            if (choiceTree != null)
            {
                choiceTree.Choices    = new List <ChoiceTreeEl>();
                choiceTree.EndChoices = new List <Choice>();
            }

            foreach (byte currMove in moves)
            {
                gameManager.NewMove(currMove, maximizingPlayer ? this : gameManager.GetOpponent(this));
                sbyte?currValue = null;

                if (gameManager.IsGameOver)
                {
                    currValue = (gameManager.Winner == this)
                           ? (sbyte)(10 + distanceFromEnd)
                           : (gameManager.Winner == null) ? (sbyte)(-distanceFromEnd) : (sbyte)(-10 - distanceFromEnd);

                    if (choiceTree != null)
                    {
                        choiceTree.EndChoices.Add(new Choice()
                        {
                            Value = (sbyte)currValue, Move = (byte)currMove
                        });
                    }
                }
                else if (distanceFromEnd > 0)
                {
                    ChoiceTree currChoiceTree = null;
                    Choice     currChoice     = null;
                    if (choiceTree != null)
                    {
                        currChoice = new Choice()
                        {
                            Move = currMove
                        };
                        currChoiceTree = new ChoiceTree();
                        choiceTree.Choices.Add(new ChoiceTreeEl()
                        {
                            Choice = currChoice, ChoiceTree = currChoiceTree
                        });
                    }
                    var choice = this.Minimax(gameManager, maxDistanceFromEnd, (byte)(distanceFromEnd - 1), alpha, beta, !maximizingPlayer, currChoiceTree);
                    if (choice != null)
                    {
                        currValue = choice.Value;
                        currValue = (currValue == 9) ? -9 : ((currValue == -9) ? 9 : currValue);

                        if (choiceTree != null)
                        {
                            currChoice.Value = (sbyte)currValue;
                        }
                    }
                    else
                    {
                        Console.WriteLine("impossible");
                    }
                }
                else
                {
                    currValue = (sbyte)(maximizingPlayer ? -9 : 9);

                    if (choiceTree != null)
                    {
                        choiceTree.EndChoices.Add(new Choice()
                        {
                            Value = (sbyte)currValue, Move = (byte)currMove
                        });
                    }
                }

                if (currValue != null)
                {
                    if (bestValue == null ||
                        (maximizingPlayer && currValue > bestValue) ||
                        (!maximizingPlayer && currValue < bestValue))
                    {
                        bestChoices.Clear();
                        bestChoices.Add(new Choice()
                        {
                            Value = (sbyte)currValue, Move = (byte)currMove
                        });
                        bestValue = currValue;
                        bestMove  = currMove;
                    }
                    else if (currValue == bestValue)
                    {
                        bestChoices.Add(new Choice()
                        {
                            Value = (sbyte)currValue, Move = (byte)currMove
                        });
                        bestValue = currValue;
                        bestMove  = currMove;
                    }

                    if (maximizingPlayer)
                    {
                        if (alpha == null || currValue > alpha)
                        {
                            alpha = currValue;
                        }
                    }
                    else
                    {
                        if (beta == null || currValue < beta)
                        {
                            beta = currValue;
                        }
                    }

                    if (alpha != null &&
                        beta != null &&
                        beta < alpha)
                    {
                        gameManager.NewMove(currMove, null);
                        break;
                    }
                }

                gameManager.NewMove(currMove, null);
            }

            Choice bestChoice;

            // Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(bestChoices));

            // Prever blocking moves
            if (bestChoices.Count > 1)
            {
                if (distanceFromEnd == maxDistanceFromEnd)
                {
                    var counts    = new Dictionary <Choice, byte>();
                    var bestCount = 0;

                    bestChoices.ForEach(choice =>
                    {
                        byte count = 0;
                        var move   = choice.Move;

                        gameManager.GetContainingPaths(move).ForEach(path =>
                        {
                            var otherCells = path.Where(cell => cell != move)
                                             .GroupBy(cell => gameManager.GetCellOwner(cell))
                                             .Select(group => new { Owner = group.Key, Cells = group.ToList() })
                                             .ToList();

                            if (otherCells.Count == 1 &&
                                otherCells[0].Owner != null)
                            {
                                count++;
                            }
                        });

                        if (count > bestCount)
                        {
                            bestCount = count;
                        }

                        counts.Add(choice, count);
                    });

                    bestChoices = counts
                                  .Keys
                                  .Where(choice => counts[choice] == bestCount)
                                  .ToList();
                }

                if (bestChoices.Count == 1)
                {
                    bestChoice = bestChoices[0];
                }
                else
                {
                    var rand    = new Random();
                    var randNum = (byte)rand.Next(bestChoices.Count);
                    bestChoice = bestChoices[randNum];
                }
            }
            else
            {
                bestChoice = new Choice()
                {
                    Value = (sbyte)bestValue, Move = (byte)bestMove
                };
            }

            if (choiceTree != null)
            {
                choiceTree.BestChoice = bestChoice;
            }

            return((bestValue == null)
                ? null
                : bestChoice);
        }