private void Worker_DoWork(object sender, DoWorkEventArgs e) { List <Action> games = new List <Action>(); List <TournamentResult> results = new List <TournamentResult>(); int completedGames = 0; foreach (var engine1 in _players) { foreach (var engine2 in _players) { if (engine1 != engine2 || _allowSelfPlay) { games.Add(() => { Stopwatch killswitch = Stopwatch.StartNew(); VolcanoGame game = new VolcanoGame(); VictoryType victory = VictoryType.None; game.OnGameOver += (p, v) => victory = v; try { game.RegisterEngine(Player.One, _engines.GetEngine(engine1)); game.RegisterEngine(Player.Two, _engines.GetEngine(engine2)); game.SecondsPerEngineMove = _secondsPerMove; game.StartNewGame(); game.ComputerPlay(); while (victory == VictoryType.None && game.CurrentState.Winner == Player.Empty && game.CurrentState.Turn < VolcanoGame.Settings.TournamentAdjudicateMaxTurns && killswitch.ElapsedMilliseconds < VolcanoGame.Settings.TournamentAdjudicateMaxSeconds * 1000) { System.Threading.Thread.Sleep(100); } var termination = TournamentTerminationType.Normal; if (game.CurrentState.Winner == Player.Empty) { if (game.CurrentState.Turn >= VolcanoGame.Settings.TournamentAdjudicateMaxTurns) { termination = TournamentTerminationType.AdjudicateMoves; } else if (killswitch.ElapsedMilliseconds >= VolcanoGame.Settings.TournamentAdjudicateMaxSeconds * 1000) { termination = TournamentTerminationType.AdjudicateTime; } } if (victory == VictoryType.ArenaAdjudication) { if (game.BackgroundError != null) { if (game.CurrentState.Winner == Player.One) { termination = TournamentTerminationType.PlayerTwoError; } else if (game.CurrentState.Winner == Player.Two) { termination = TournamentTerminationType.PlayerOneError; } else { termination = TournamentTerminationType.Error; } } else { termination = TournamentTerminationType.IllegalMove; } } lock (results) { results.Add(new TournamentResult(game, engine1, engine2, termination, killswitch.ElapsedMilliseconds)); } } catch (Exception ex) { using (StreamWriter w = new StreamWriter("errors.txt", true)) { w.WriteLine("Failed to play \"" + engine1 + "\" and \"" + engine2 + "\"! :: " + ex.Message); } try { var termination = game?.CurrentState?.Player == Player.One ? TournamentTerminationType.PlayerOneError : TournamentTerminationType.PlayerTwoError; lock (results) { results.Add(new TournamentResult(game, engine1, engine2, termination, killswitch.ElapsedMilliseconds)); } } catch (Exception eotw) { using (StreamWriter w2 = new StreamWriter("errors.txt", true)) { w2.WriteLine("Failed to log result! :: " + eotw.Message); } } } completedGames++; worker.ReportProgress(0, new TournamentStatus(completedGames, totalGames)); }); } } } for (int r = 0; r < _rounds; r++) { Parallel.ForEach(games, x => x()); } using (StreamWriter w = new StreamWriter(_gameDataFile)) { w.WriteLine("Player_One,Player_Two,Winner,Termination,Total_Moves,Total_Milliseconds,Starting_Tile_Index,Transcript,Winning_Path,Win_Condition,Seconds_Per_Move"); foreach (var result in results) { string gameResult = "Draw"; if (result.PlayerOneScore > result.PlayerTwoScore) { gameResult = "One"; } if (result.PlayerTwoScore > result.PlayerOneScore) { gameResult = "Two"; } string transcript = ""; if (result.Moves.Count > 0) { try { transcript = result.Moves.Select(x => x.Tile + (VolcanoGame.Settings.IndicateTranscriptMoveType && x.Addition ? "+" : "")).Aggregate((c, n) => c + " " + n); } catch (Exception ex) { transcript = ex.Message; } } string winningPath = ""; if (result.WinningPath != null && result.WinningPath.Count > 0) { try { winningPath = result.WinningPath.Select(x => Constants.TileNames[x]).Aggregate((c, n) => c + " " + n); } catch (Exception ex) { winningPath = ex.Message; } } string winCondition = ""; if (result.WinCondition == 1) { winCondition = "Tie-breaker"; } else { winCondition = "Normal Win (no tie-breaker)"; } w.WriteLine(result.PlayerOne + "," + result.PlayerTwo + "," + gameResult + "," + result.Termination.ToString() + "," + result.TotalMoves + "," + result.ElapsedMilliseconds + "," + result.FirstTile + "," + transcript + "," + winningPath + "," + winCondition + "," + _secondsPerMove); } } List <TournamentResultLine> lines = new List <TournamentResultLine>(); foreach (var engine1 in _players) { decimal score = 0m; TournamentResultLine line = new TournamentResultLine(); line.Name = engine1; foreach (var engine2 in _players) { if (engine1 != engine2 || _allowSelfPlay) { decimal wins = results.Where(x => x.PlayerOne == engine1 && x.PlayerTwo == engine2).Sum(x => x.PlayerOneScore) + results.Where(x => x.PlayerOne == engine2 && x.PlayerTwo == engine1).Sum(x => x.PlayerTwoScore); score += wins; line.Data.Add(engine2, wins); } else { line.Data.Add(engine2, -1); } } decimal total = results.Where(x => x.PlayerOne == engine1 || x.PlayerTwo == engine1).Count(); line.TotalScore = score; line.TotalPercentage = score * 100m / total; line.Score = score; lines.Add(line); } lines.Sort((c, n) => n.Sort.CompareTo(c.Sort)); List <string> names = new List <string>(); for (int i = 0; i < lines.Count; i++) { lines[i].CrossTableName = (i + 1) + ". " + lines[i].Name; names.Add((i + 1).ToString()); lines[i].NeustadtlScore = 0m; foreach (var opponent in lines) { if (opponent.Name != lines[i].Name) { lines[i].NeustadtlScore += opponent.TotalScore * lines[i].Data[opponent.Name]; } } } // It doesn't make sense to save a cross table when there's only one person competing if (!string.IsNullOrEmpty(_crossTableFile) && lines.Count > 1) { using (StreamWriter w = new StreamWriter(_crossTableFile)) { // cs = common score // ns = neustadtl score (figures in strength of opposition) w.WriteLine("," + names.Aggregate((c, n) => c + "," + n) + ",cs,ns"); foreach (var player in lines) { w.Write(player.CrossTableName); foreach (var opponent in lines) { string val = player.Data[opponent.Name] >= 0 ? player.Data[opponent.Name].ToString() : ""; w.Write("," + val); } w.Write(", " + player.TotalScore); w.Write(", " + player.NeustadtlScore.ToString("0.00")); w.WriteLine(); } } } }
private void Worker_DoWork(object sender, DoWorkEventArgs e) { results = new List <TournamentResult>(); completedGames = 0; timeouts = 0; if (_tournamentType == TournamentType.RoundRobin) { games = new List <Action>(); foreach (var engine1 in _players) { foreach (var engine2 in _players) { if (engine1 != engine2 || _allowSelfPlay) { QueueGame(engine1, engine2); } } } for (int r = 0; r < _rounds; r++) { Parallel.ForEach(games, x => x()); } } else if (_tournamentType == TournamentType.Swiss) { var rand = new Random(); var swissPlayers = _players.Select(x => new SwissPlayer { Engine = x, Score = 0 }).ToList(); for (int r = 0; r < _rounds; r++) { swissPlayers = swissPlayers.OrderByDescending(x => x.Score).ThenBy(x => rand.Next()).ToList(); games = new List <Action>(); // If there's an odd number of players, the last player doesn't play for (int i = 0; i < ((swissPlayers.Count % 2 == 1) ? swissPlayers.Count - 1 : swissPlayers.Count); i += 2) { // Play both colors QueueGame(swissPlayers[i].Engine, swissPlayers[i + 1].Engine); QueueGame(swissPlayers[i + 1].Engine, swissPlayers[i].Engine); } Parallel.ForEach(games, x => x()); // Update scores for next round for (int i = 0; i < ((swissPlayers.Count % 2 == 1) ? swissPlayers.Count - 1 : swissPlayers.Count); i++) { swissPlayers[i].Score = results.Where(x => x.PlayerOne == swissPlayers[i].Engine).Sum(x => x.PlayerOneScore); swissPlayers[i].Score += results.Where(x => x.PlayerTwo == swissPlayers[i].Engine).Sum(x => x.PlayerTwoScore); } // If someone didn't play, they get a half point bye (per game) for the sake of pairings if (swissPlayers.Count % 2 == 1) { swissPlayers[swissPlayers.Count - 1].Score++; } } } using (StreamWriter w = new StreamWriter(_gameDataFile)) { w.WriteLine("Player One,Player Two,Winner,Termination,Total Moves,Total Milliseconds,Starting Tile Index,Transcript,Winning Path One,Winning Path Two"); foreach (var result in results) { string gameResult = "Draw"; if (result.PlayerOneScore > result.PlayerTwoScore) { gameResult = "One"; } if (result.PlayerTwoScore > result.PlayerOneScore) { gameResult = "Two"; } string transcript = ""; if (result.Moves.Count > 0) { try { transcript = result.Moves.Select(x => x.Tile + (VolcanoGame.Settings.IndicateTranscriptMoveType && x.Addition ? "+" : "")).Aggregate((c, n) => c + " " + n); } catch (Exception ex) { transcript = ex.Message; } } string winningPathOne = GetWinningPath(result.WinningPathOne); string winningPathTwo = GetWinningPath(result.WinningPathTwo); w.WriteLine(result.PlayerOne + "," + result.PlayerTwo + "," + gameResult + "," + result.Termination.ToString() + "," + result.TotalMoves + "," + result.ElapsedMilliseconds + "," + result.FirstTile + "," + transcript + "," + winningPathOne + "," + winningPathTwo); } } List <TournamentResultLine> lines = new List <TournamentResultLine>(); foreach (var engine1 in _players) { decimal score = 0m; TournamentResultLine line = new TournamentResultLine(); line.Name = engine1; foreach (var engine2 in _players) { if (engine1 != engine2 || _allowSelfPlay) { decimal wins = results.Where(x => x.PlayerOne == engine1 && x.PlayerTwo == engine2).Sum(x => x.PlayerOneScore) + results.Where(x => x.PlayerOne == engine2 && x.PlayerTwo == engine1).Sum(x => x.PlayerTwoScore); score += wins; line.Data.Add(engine2, wins); } else { line.Data.Add(engine2, -1); } } decimal total = results.Where(x => x.PlayerOne == engine1 || x.PlayerTwo == engine1).Count(); line.TotalScore = score; line.TotalPercentage = score * 100m / total; line.Score = score; lines.Add(line); } lines.Sort((c, n) => n.Sort.CompareTo(c.Sort)); List <string> names = new List <string>(); for (int i = 0; i < lines.Count; i++) { lines[i].CrossTableName = (i + 1) + ". " + lines[i].Name; names.Add((i + 1).ToString()); lines[i].NeustadtlScore = 0m; foreach (var opponent in lines) { if (opponent.Name != lines[i].Name) { lines[i].NeustadtlScore += opponent.TotalScore * lines[i].Data[opponent.Name]; } } } // It doesn't make sense to save a cross table when there's only one person competing if (!string.IsNullOrEmpty(_crossTableFile) && lines.Count > 1) { using (StreamWriter w = new StreamWriter(_crossTableFile)) { // cs = common score // ns = neustadtl score (figures in strength of opposition) w.WriteLine("," + names.Aggregate((c, n) => c + "," + n) + ",cs,ns"); foreach (var player in lines) { w.Write(player.CrossTableName); foreach (var opponent in lines) { string val = player.Data[opponent.Name] >= 0 ? player.Data[opponent.Name].ToString() : ""; w.Write("," + val); } w.Write(", " + player.TotalScore); w.Write(", " + player.NeustadtlScore.ToString("0.00")); w.WriteLine(); } } } }