public NewMainPage() { this.InitializeComponent(); AvailablePlayer game = new AvailablePlayer("", typeof(DefaultPlayer), "Default - Easy", PlayerAlgorithm.Easy); AllPlayers.Add(game); PlayerOne = game; game = new AvailablePlayer("-usedroptable", typeof(DefaultPlayer), "Default - Hard", PlayerAlgorithm.Hard); AllPlayers.Add(game); PlayerTwo = game; game = new AvailablePlayer("", typeof(RandomPlayer), "Random Player", PlayerAlgorithm.Random); AllPlayers.Add(game); game = new AvailablePlayer("", typeof(CountingPlayer), "Improved Counting Player", PlayerAlgorithm.ImprovedCounting); AllPlayers.Add(game); }
/// <summary> /// Create and run each of the games. Keep track of how long it takes. return the stats. /// </summary> /// <param name="iterations"></param> /// <param name="logger"></param> /// <param name="playerOne"></param> /// <param name="playerTwo"></param> /// <returns></returns> private async Task <GameStats> RunGames(int iterations, bool useLog, AvailablePlayer playerOne, AvailablePlayer playerTwo) { var watch = System.Diagnostics.Stopwatch.StartNew(); ConcurrentBag <Game> gameBag = new ConcurrentBag <Game>(); ConcurrentBag <Task> taskBag = new ConcurrentBag <Task>(); bool playerOneStarts = true; if (useLog) { // // this has to happen on the main thread, so it can't happen inside the worker threads // // this lets us put the documents in whatever location we want instead of inside the private storage area for the app, which is burried pretty deep in the // disk heirarchy and hard to get to. You only have to do this once per machine though. string content = "After clicking on \"Close\" pick the default location for all your CribbageAi saved state"; _saveDirectory = await StaticHelpers.GetSaveFolder(content, "CribbageAI"); _saveDirectory = await _saveDirectory.CreateFolderAsync(DateTime.Now.ToString("hh_mm_ss dd_MM_yyyy")); SaveDirectory = _saveDirectory.Path; } else { _saveDirectory = null; SaveDirectory = ""; } NotifyPropertyChanged("SaveDirectory"); // // this uses Parallel.For to create 1 thread per processor. Each thread creates a tasks and starts running it. // The task runs a game to completion, logging everything to a file. I do it this way so that I can wait for the // logs to finish writing -- I found that if I did it outside of a Task list, it would finish the log writes async Parallel.For(0, iterations, i => { taskBag.Add(Task.Run(async() => { Player player1 = (Player)Activator.CreateInstance(playerOne.GameType); player1.Init(playerOne.Parameters); player1.PlayerAlgorithm = playerOne.PlayerAlgorithm; Player player2 = (Player)Activator.CreateInstance(playerTwo.GameType); player2.Init(playerTwo.Parameters); player2.PlayerAlgorithm = playerTwo.PlayerAlgorithm; Game game = new Game(i, player1, player2, playerOneStarts); await game.Init(useLog, _saveDirectory); // // alternate who deals - looks like if you use the same algorithm, dealer *loses* 60% of the time! if ((bool)AlternateWhoStarts) { if (playerOneStarts) { playerOneStarts = false; } else { playerOneStarts = true; } } gameBag.Add(game); await game.PlayGame(); } )); }); // // wait for all the games to complete -- this includes the log writes Task.WaitAll(taskBag.ToArray()); watch.Stop(); // // add up some stats GameStats stats = new GameStats(); int totalP1LostScore = 0; int totalP2LostScore = 0; stats.WallClocktime = watch.ElapsedMilliseconds; stats.MSPerGame = stats.WallClocktime / (double)iterations; foreach (var game in gameBag) { if (game == null) { continue; } // // this calculates the number of times each player one and the average of their *losing* score // if (game.PlayerOne.Winner) { totalP2LostScore += game.PlayerTwo.Score; stats.PlayerOneWin++; } else { stats.PlayerTwoWin++; totalP1LostScore += game.PlayerOne.Score; } stats.AverageCountPointsPlayerOne += game.AverageScore(PlayerName.PlayerOne, ScoreType.Count); stats.AverageCountPointsPlayerTwo += game.AverageScore(PlayerName.PlayerTwo, ScoreType.Count); stats.AverageHandPointsPlayerOne += game.AverageScore(PlayerName.PlayerOne, ScoreType.Hand); stats.AverageHandPointsPlayerTwo += game.AverageScore(PlayerName.PlayerTwo, ScoreType.Hand); stats.AverageCribPointsPlayerOne += game.AverageScore(PlayerName.PlayerOne, ScoreType.Crib); stats.AverageCribPointsPlayerTwo += game.AverageScore(PlayerName.PlayerTwo, ScoreType.Crib); } stats.AverageCountPointsPlayerOne /= gameBag.Count; stats.AverageCountPointsPlayerTwo /= gameBag.Count; stats.AverageHandPointsPlayerOne /= gameBag.Count; stats.AverageHandPointsPlayerTwo /= gameBag.Count; stats.AverageCribPointsPlayerOne /= gameBag.Count; stats.AverageCribPointsPlayerTwo /= gameBag.Count; stats.PlayerOneWinPercent = Math.Round((double)stats.PlayerOneWin / (double)(iterations) * 100, 2); stats.PlayerTwoWinPercent = Math.Round((double)stats.PlayerTwoWin / (double)(iterations) * 100, 2); stats.AverageScorePlayerOne = Math.Round(totalP1LostScore / (double)(stats.PlayerTwoWin), 2); stats.AverageScorePlayerTwo = Math.Round(totalP2LostScore / (double)(stats.PlayerOneWin), 2); return(stats); }