예제 #1
0
        /// <summary>
        /// Alphabeta search function
        /// </summary>
        /// <param name="depth">depth of the search</param>
        /// <param name="minOrMax">1 = maximize / -1 = minimize </param>
        /// <param name="parentValue">parent score</param>
        /// <param name="whiteTurn">is white playing ?</param>
        /// <param name="amIWhite">optimize for white or black?</param>
        /// <returns>value of board and tuple containing the best move to play</returns>
        private Tuple <double, Tuple <int, int> > alphabeta(int depth, int minOrMax, double parentValue, bool whiteTurn, bool amIWhite)
        {
            // minOrMax = 1 : maximize
            // minOrMax = -1 : minimize

            possibleMoves(whiteTurn);

            if (depth == 0 || canMove.Count == 0)
            {
                return(new Tuple <double, Tuple <int, int> >(eval(amIWhite), new Tuple <int, int>(-1, -1)));
            }

            double optVal = minOrMax * (-10000000);

            Tuple <int, int> optOp = null;

            List <Tuple <int, int> > moves = canMove.ToList();

            foreach (Tuple <int, int> op in moves)
            {
                // create new state
                OthelloBoard newBoard = new OthelloBoard(GetBoard());
                newBoard.PlayMove(op.Item1, op.Item2, whiteTurn);

                //alphabeta on the new state with depth - 1
                Tuple <double, Tuple <int, int> > result = newBoard.alphabeta(depth - 1, -minOrMax, optVal, !whiteTurn, amIWhite);

                if (result.Item1 * minOrMax > optVal * minOrMax)
                {
                    optVal = result.Item1;
                    optOp  = op;

                    if (optVal * minOrMax > parentValue * minOrMax)
                    {
                        break;
                    }
                }
            }
            return(new Tuple <double, Tuple <int, int> >(optVal, optOp));
        }
        /// <summary>
        /// Console application that plays an othello game with 2 AIs implementing IPlayable interface
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            #region LOAD_IAs

            //find all DLLs with name starting with "OthelloIA" in the executable folder
            List <Assembly> IAPlayers = new List <Assembly>();
            string          path      = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            foreach (string dll in Directory.GetFiles(path, "OthelloIA*.dll"))
            {
                IAPlayers.Add(Assembly.LoadFile(dll));
            }
            foreach (Assembly assembly in IAPlayers)
            {
                Assembly.Load(assembly.FullName);
            }
            #endregion

            // 1. Connect two players by either loading a)dynamically or b)statically the teams assemblies
            IPlayable.IPlayable player1 = null, player2 = null, serverController = null;

            //a)Recover the types from the DLL assemblies using reflection for the 2 players
            if (IAPlayers.Count >= 2)
            {
                Type[] T1 = IAPlayers[0].GetTypes();
                for (int i = 0; i < T1.Count(); i++)
                {
                    if (T1[i].Name.Contains("Board"))                                   // the IA's class that implements IPlayable must have "Board" in its name. E.g OthelloBoard, TheBoard, MyBoard, ...
                    {
                        player1 = (IPlayable.IPlayable)Activator.CreateInstance(T1[i]); // requires a default constructore
                    }
                }
                if (player1 == null)
                {
                    player1 = new OthelloIA2.OthelloBoard();
                }
                Type[] T2 = IAPlayers[1].GetTypes();        //or    GetType ("OthelloIA2.OthelloBoard");
                for (int i = 0; i < T2.Count(); i++)
                {
                    if (T2[i].Name.Contains("Board"))                                   // the IA's class that implements IPlayable must have "Board" in its name. E.g OthelloBoard, TheBoard, MyBoard, ...
                    {
                        player2 = (IPlayable.IPlayable)Activator.CreateInstance(T2[i]); // requires a default constructore
                    }
                }
                if (player2 == null)
                {
                    player2 = new OthelloIA2.OthelloBoard();
                }
            }
            else // b) add a reference to your class assembly in the project and instantiate it
            {
                player1 = new AlphaBetaTeam11Library.LogicBoard();   // for example
                player2 = new AlphaBetaTeam11Library.LogicBoard();   // for example
            }
            // The Game controller
            serverController = new OthelloLib.Board();             //reference interne au projet

            // Initialize the board for initial display
            int[,] theBoard = (serverController as OthelloLib.Board).GetBoard();

            //UI PART draw the 3 boards: reference, player1, player2
            Console.BackgroundColor = ConsoleColor.DarkBlue;
            Console.WriteLine("REFEREE : " + serverController.GetName());
            Console.WriteLine("TEAM 1  : " + player1.GetName());
            Console.WriteLine("TEAM 2  : " + player2.GetName());
            (serverController as OthelloLib.Board).DrawBoard();
            //DrawBoard(player1.GetBoard(), player1.GetName(), player1.GetBlackScore(), player1.GetWhiteScore());
            //DrawBoard(player2.GetBoard(), player2.GetName(), player2.GetBlackScore(), player2.GetWhiteScore());

            #region GAMELOOP
            //GAME LOOP
            int[,] refBoard = (serverController as OthelloLib.Board).GetBoard();
            int[,] board1   = player1.GetBoard();
            int[,] board2   = player2.GetBoard();

            Tuple <int, int> playerMove = null;
            int  passCount = 0;
            bool testPlayer1, testPlayer2;
            bool whitePlays = false;
            IPlayable.IPlayable activePlayer = player1;     // player 1 begins playing black

            while (boardCompare(refBoard, board1) && boardCompare(refBoard, board2) && (passCount < 2))
            {
                int totalScore = serverController.GetBlackScore() + serverController.GetWhiteScore();
                Console.Clear();
                try
                {
                    playerMove = activePlayer.GetNextMove(theBoard, 5, whitePlays);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message + "\n\n" + e.StackTrace);
                }
                Console.Write(whitePlays ? "\n[O]" : "\n[X]");
                // check move validity
                if ((playerMove.Item1 == PASS) && (playerMove.Item1 == PASS))
                {
                    Console.WriteLine("PASS");
                    passCount++;
                    ///TODO verify if no move was possible (no playable move otherwise display a msg or throw something
                    if ((serverController as OthelloLib.Board).GetPossibleMoves(whitePlays).Count != 0)
                    {
                        throw new Exception($"you shall NOT PASS {activePlayer}!");
                    }
                }
                else
                {
                    passCount = 0;
                    Console.WriteLine($"{activePlayer.GetName()}: {(char)('A' + playerMove.Item1)}{1 + playerMove.Item2}");
                    // check validity
                    if ((serverController as OthelloLib.Board).IsPlayable(playerMove, whitePlays))

                    {
                        Console.WriteLine("Coup valide");
                        // play the move for both players and referee
                        serverController.PlayMove(playerMove.Item1, playerMove.Item2, whitePlays);
                        testPlayer1 = player1.PlayMove(playerMove.Item1, playerMove.Item2, whitePlays);  // no verification here
                        testPlayer1 = player2.PlayMove(playerMove.Item1, playerMove.Item2, whitePlays);  // no verification here

                        // compare boards for validity
                        refBoard    = (serverController as OthelloLib.Board).GetBoard();
                        board1      = player1.GetBoard();
                        board2      = player2.GetBoard();
                        testPlayer1 = boardCompare(refBoard, board1);
                        testPlayer2 = boardCompare(refBoard, board2);
                        if (testPlayer1 && testPlayer2)  // we only need to draw one board now
                        {
                            (serverController as OthelloLib.Board).DrawBoard();
                        }
                        else if (!testPlayer1)
                        {
                            throw new Exception("Board state of player 1 is incorrect");
                        }
                        else if (!testPlayer2)
                        {
                            throw new Exception("Board state of player 2 is incorrect");
                        }
                    }
                }
                // SWAP players and color
                whitePlays   = !whitePlays;
                activePlayer = (activePlayer == player1) ? player2 : player1;

                System.Threading.Thread.Sleep(2000); // slow down game speed or //Console.ReadKey();
            } // end of GAMELOOP
            #endregion

            if (serverController.GetBlackScore() > serverController.GetWhiteScore())
            {
                Console.WriteLine($"{player1.GetName()} WINS!" + player1.GetBlackScore() + " - " + player1.GetWhiteScore());
            }
            else
            {
                Console.WriteLine($"{player2.GetName()} WINS!" + player1.GetWhiteScore() + " - " + player1.GetBlackScore());
            }
            Console.WriteLine("END OF GAME, FAREWELL!");
            Console.ReadKey();
        }