public TournamentResult(VolcanoGame state, string playerOne, string playerTwo, TournamentTerminationType termination, long milliseconds) { PlayerOne = playerOne; PlayerTwo = playerTwo; Termination = termination; ElapsedMilliseconds = milliseconds; if (state.CurrentState.Winner == Player.One || termination == TournamentTerminationType.PlayerTwoError) { PlayerOneScore = 1m; } else if (state.CurrentState.Winner == Player.Two || termination == TournamentTerminationType.PlayerOneError) { PlayerTwoScore = 1m; } else { // Draw PlayerOneScore = 0.5m; PlayerTwoScore = 0.5m; } try { Moves = state.GetDetailedMoveList(); } catch (Exception ex) { using (StreamWriter w = new StreamWriter("errors.txt", true)) { w.WriteLine("Failed to copy move history! :: " + ex.Message); } Moves = new List <Move>(); } TotalMoves = state?.CurrentState?.Turn ?? 0; if (Moves.Count > 0) { FirstTile = Moves[0].Location; } WinningPath = state?.CurrentState?.WinningPath ?? new List <int>(); WinCondition = state?.CurrentState?.WinCondition ?? new int(); }
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 QueueGame(string engine1, string engine2) { 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), true); game.RegisterEngine(Player.Two, _engines.GetEngine(engine2), true); 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); } game.ForceStop(); 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; } } else if (victory == VictoryType.OpponentTimeout) { termination = TournamentTerminationType.Timeout; } 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, timeouts)); }); }