/// <summary> /// Method to test the IA is complying with Othello rules /// BROKEN (does not work) /// Probably that putting random pawns all over the board is not a good way to /// initialize a playable board /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void MenuTestIA_Click(object sender, RoutedEventArgs e) { Referee = new OthelloLib.Board(); if ((lbIABlack.SelectedIndex == -1) || (tournamentPlayers.Count == 0)) { return; } IPlayable.IPlayable testBlackPlayer = (IPlayable.IPlayable)Activator.CreateInstance(tournamentPlayers[lbIABlack.SelectedIndex].GetType()); UI.Title = "TEST RUNNING"; Tuple <int, int> move; bool blackTurn = true; int percent = 0; for (int t = 0; t < 100; t++) { // generate a random board position int[,] testBoard = getRandomBoard(); move = Referee.GetNextMove(testBoard, 5, blackTurn); if (move.Item1 != -1) { testBlackPlayer.PlayMove(move.Item1, move.Item2, blackTurn); //verify Referee.PlayMove(move.Item1, move.Item2, blackTurn); int[,] board1 = testBlackPlayer.GetBoard(); int[,] board2 = Referee.GetBoard(); if (boardCompare(board1, board2)) { percent++; } UI.Title += "."; } else { percent++; UI.Title += "X"; } System.Threading.Thread.Sleep(30); } UI.Title = $"TEST COMPLETE ({percent}%)"; }
/// <summary> /// This method will perform the game loop /// NOTE: it is required for this method to be async to allow for await /// otherwise it will lock the main(UI) thread and the game status is not /// visually updated (refreshed) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void MenuStartGame_Click(object sender, RoutedEventArgs e) { if ((tournamentBlackPlayer == null) || (tournamentWhitePlayer == null)) { System.Windows.MessageBox.Show("You NEED 2 IAs to play a match!"); return; } // to prevent crash with some IA when playing a second time tournamentBlackPlayer = (IPlayable.IPlayable)Activator.CreateInstance(tournamentPlayers[lbIABlack.SelectedIndex].GetType()); tournamentWhitePlayer = (IPlayable.IPlayable)Activator.CreateInstance(tournamentPlayers[lbIAWhite.SelectedIndex].GetType()); RestartGame(); Referee = new OthelloLib.Board(); int[,] refBoard = (Referee as OthelloLib.Board).GetBoard(); int[,] board1 = refBoard; //tournamentBlackPlayer.GetBoard(); // PLAYER 1 int[,] board2 = refBoard; //tournamentWhitePlayer.GetBoard(); // PLAYER 2 team1.Foreground = new SolidColorBrush(Color.FromRgb(0, 0, 0)); team2.Foreground = new SolidColorBrush(Color.FromRgb(0, 0, 0)); Tuple <int, int> playerMove = null; int passCount = 0; bool testPlayer1, testPlayer2; bool whitePlays = false; IPlayable.IPlayable activePlayer = tournamentBlackPlayer; // player 1 begins playing black //GAME LOOP while (boardCompare(refBoard, board1) && boardCompare(refBoard, board2) && (passCount < 2)) { int totalScore = Referee.GetBlackScore() + Referee.GetWhiteScore(); await Task.Delay(whitePlays? 100 : 100); // slow down game speed try { playerMove = activePlayer.GetNextMove(refBoard, 5, whitePlays); } catch (Exception ex) { Console.WriteLine(ex.Message + "\n\n" + ex.StackTrace); } // check move validity if (playerMove == null) { System.Windows.MessageBox.Show($" {activePlayer.GetName()} a renvoyé un coup NULL\nFIN DE LA PARTIE"); return; } if ((playerMove.Item1 == PASS) && (playerMove.Item2 == PASS)) { Console.WriteLine("PASS"); passCount++; ///TODO verify if no move was possible (no playable move otherwise display a msg or throw something if ((Referee as OthelloLib.Board).GetPossibleMoves(whitePlays).Count != 0) { System.Windows.MessageBox.Show($" {activePlayer.GetName()} a passé son tour de manière illégale\nFIN DE LA PARTIE"); return; } } else { passCount = 0; Console.WriteLine($"{activePlayer.GetName()}: {(char)('A' + playerMove.Item1)}{1 + playerMove.Item2}"); // check validity if ((Referee as OthelloLib.Board).IsPlayable(playerMove, whitePlays)) { Console.WriteLine($"Coup valide de {activePlayer}"); // play the move for both players and referee Referee.PlayMove(playerMove.Item1, playerMove.Item2, whitePlays); testPlayer1 = tournamentBlackPlayer.PlayMove(playerMove.Item1, playerMove.Item2, whitePlays); // no verification here testPlayer1 = tournamentWhitePlayer.PlayMove(playerMove.Item1, playerMove.Item2, whitePlays); // no verification here // compare boards for validity refBoard = (Referee as OthelloLib.Board).GetBoard(); board1 = tournamentBlackPlayer.GetBoard(); board2 = tournamentWhitePlayer.GetBoard(); testPlayer1 = boardCompare(refBoard, board1); testPlayer2 = boardCompare(refBoard, board2); if (testPlayer1 && testPlayer2) // the move is valid { PlayMove(playerMove.Item1, playerMove.Item2); UI.Title = $"BLACK:{Referee.GetBlackScore()} WHITE:{Referee.GetWhiteScore()}"; //System.Windows.MessageBox.Show("."); await Task.Delay(delay); } else if (!testPlayer1) { System.Windows.MessageBox.Show($"Board state of {tournamentBlackPlayer.GetName()}is incorrect"); throw new Exception($"Board state of {tournamentBlackPlayer.GetName()}is incorrect"); } else if (!testPlayer2) { System.Windows.MessageBox.Show($"Board state of {tournamentWhitePlayer.GetName()}is incorrect"); throw new Exception($"Board state of {tournamentWhitePlayer.GetName()} is incorrect"); } } else { Console.WriteLine($"COUP INVALIDE {(char)(playerMove.Item1+'A')}{playerMove.Item2+1}"); if (whitePlays == true) { team2.Foreground = new SolidColorBrush(Color.FromRgb(255, 100, 100)); System.Windows.MessageBox.Show($"COUP INVALIDE de {tournamentWhitePlayer.GetName()} {(char)playerMove.Item1 + 'A'}{playerMove.Item2 + 1}\nFIN DE LA PARTIE"); return; } else { team1.Foreground = new SolidColorBrush(Color.FromRgb(255, 100, 100)); System.Windows.MessageBox.Show($"COUP INVALIDE de {tournamentBlackPlayer.GetName()} {(char)(playerMove.Item1 + 'A')}{playerMove.Item2 + 1}\nFIN DE LA PARTIE"); return; } } } // SWAP players and color whitePlays = !whitePlays; activePlayer = (activePlayer == tournamentBlackPlayer) ? tournamentWhitePlayer : tournamentBlackPlayer; } // end of GAMELOOP }
/// <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(); }