/// <summary> /// Returns the amount of wins the challenger got (from 100) /// </summary> private static int CalculateChallengerWinsFrom100(IMoveDecisionMaker best, IMoveDecisionMaker challenger) { int challengerWins = 0; Parallel.For(0, 200, (i) => { var state = new SimulationState(SimulationHelpers.GetRandomPieces(i / 2), 0); state.ActivePlayer = i % 2; state.Fidelity = SimulationFidelity.NoPiecePlacing; //TODO: May need a cheaper placement engine var runner = new SimulationRunner(state, new PlayerDecisionMaker(best, null), new PlayerDecisionMaker(challenger, null)); while (!state.GameHasEnded) { runner.PerformNextStep(); } if (state.WinningPlayer == 1) { Interlocked.Increment(ref challengerWins); } }); return(challengerWins); }
public double CalculateValueOfPurchasing(SimulationState state, int pieceIndex, PieceDefinition piece) { var offset = OffsetForPosition(state); var value = piece.TotalUsedLocations * _value[offset + UsedLocationUtilityOffset]; value += piece.ButtonCost * _value[offset + ButtonCostUtilityOffset]; value += piece.TimeCost * _value[offset + TimeCostUtilityOffset]; //TODO: Should we have piece income and total income utilities? value += SimulationHelpers.ButtonIncomeAmountAfterPosition(state.PlayerPosition[state.ActivePlayer]) * piece.ButtonsIncome * _value[offset + IncomeUtilityOffset]; value += SimulationHelpers.ButtonIncomeAmountAfterPosition(state.PlayerPosition[state.ActivePlayer]) * piece.ButtonsIncome * piece.ButtonsIncome * _value[offset + IncomeSquaredUtilityOffset]; //TODO: Should this be boolean or vary by difference in location? if (state.PlayerPosition[state.NonActivePlayer] >= (state.PlayerPosition[state.ActivePlayer] + piece.TimeCost)) { value += _value[offset + GetAnotherTurnUtilityOffset]; } //TODO: Should this be boolean or vary by income amount? if (SimulationHelpers.ButtonIncomeAmountAfterPosition(state.PlayerPosition[state.ActivePlayer]) != SimulationHelpers.ButtonIncomeAmountAfterPosition(Math.Min(SimulationState.EndLocation, state.PlayerPosition[state.ActivePlayer] + piece.TimeCost))) { value += _value[offset + ReceiveIncomeUtilityOffset]; } return(value); //TODO Clamp? Divide by total utilities? }
public void AlwaysAdvanceToEndNoPiecePlacing() { var state = new SimulationState(SimulationHelpers.GetRandomPieces(), 0); state.Fidelity = SimulationFidelity.NoPiecePlacing; var runner = new SimulationRunner(state, new PlayerDecisionMaker(AlwaysAdvanceMoveMaker.Instance, null), new PlayerDecisionMaker(AlwaysAdvanceMoveMaker.Instance, null)); //The game definitely ends within 200 steps for (var i = 0; i < 200 && !state.GameHasEnded; i++) { runner.PerformNextStep(); Assert.InRange(state.PlayerPosition[0], 0, SimulationState.EndLocation); Assert.InRange(state.PlayerPosition[1], 0, SimulationState.EndLocation); } //Both players should be at the end Assert.Equal(SimulationState.EndLocation, state.PlayerPosition[0]); Assert.Equal(SimulationState.EndLocation, state.PlayerPosition[1]); //Game should have ended Assert.True(state.GameHasEnded); //Players should have one button for each place they moved Assert.Equal(SimulationState.EndLocation + SimulationState.PlayerStartingButtons, state.PlayerButtonAmount[0]); Assert.Equal(SimulationState.EndLocation + SimulationState.PlayerStartingButtons, state.PlayerButtonAmount[1]); //non-starting player should have won by collecting all of the leather patches (https://boardgamegeek.com/thread/1703957/how-break-stalemate) Assert.Equal(1, state.WinningPlayer); //Check the ending points are correct Assert.Equal(SimulationState.EndLocation - BoardState.Width * BoardState.Height * 2 + SimulationState.PlayerStartingButtons, state.CalculatePlayerEndGameWorth(0)); Assert.Equal(SimulationState.EndLocation - BoardState.Width * BoardState.Height * 2 + SimulationState.PlayerStartingButtons + 2 * SimulationState.LeatherPatches.Length, state.CalculatePlayerEndGameWorth(1)); }
public static GameState CreateGameState(SimulationState sim) { var res = new GameState(); var ap = sim.ActivePlayer; var na = sim.NonActivePlayer; res.Observation.Add(sim.PlayerButtonIncome[ap] / ButtonIncomeScale); res.Observation.Add(sim.PlayerButtonIncome[na] / ButtonIncomeScale); res.Observation.Add(sim.PlayerButtonAmount[ap] / ButtonAmountScale); res.Observation.Add(sim.PlayerButtonAmount[na] / ButtonAmountScale); res.Observation.Add(sim.PlayerBoardUsedLocationsCount[ap] / UsedLocationsScale); res.Observation.Add(sim.PlayerBoardUsedLocationsCount[na] / UsedLocationsScale); res.Observation.Add(sim.PlayerPosition[ap] / PlayerPositionScale); res.Observation.Add(sim.PlayerPosition[na] / PlayerPositionScale); res.Observation.Add(SimulationHelpers.ButtonIncomeAmountAfterPosition(sim.PlayerPosition[ap]) / IncomesRemainingScale); res.Observation.Add(SimulationHelpers.ButtonIncomeAmountAfterPosition(sim.PlayerPosition[na]) / IncomesRemainingScale); for (var i = 0; i < LookAheadPieceAmount; i++) { var piece = Helpers.GetNextPiece(sim, i); res.Observation.Add(piece.ButtonCost / PieceButtonCostScale); res.Observation.Add(piece.ButtonsIncome / PieceButtonIncomeScale); res.Observation.Add(piece.TotalUsedLocations / PieceUsedLocationsScale); } return(res); }
private static void RunMoveMakerForPerformance() { var sw = Stopwatch.StartNew(); #if PERF_PARALLEL Parallel.For(0, 10, (run) => //5900 #else for (var run = 0; run < 4; run++) //13000 #endif { //var a = new PlayerDecisionMaker(new MoveOnlyMonteCarloTreeSearchMoveMaker(10000), PlacementMaker.FirstPossibleInstance); //var b = new PlayerDecisionMaker(new MoveOnlyMonteCarloTreeSearchMoveMaker(5000), PlacementMaker.FirstPossibleInstance); var mctsA = new MonteCarloTreeSearchMoveMaker(5000, TuneableUtilityMoveMaker.Tuning1, new TightBoardEvaluator(true), 2); var a = new PlayerDecisionMaker(mctsA, new PlacementMaker(mctsA.PlacementStrategy)); var mctsB = new MonteCarloTreeSearchMoveMaker(5000, TuneableUtilityMoveMaker.Tuning1, new TightBoardEvaluator(true), 2); var b = new PlayerDecisionMaker(mctsB, new PlacementMaker(mctsB.PlacementStrategy)); var state = new SimulationState(SimulationHelpers.GetRandomPieces(run / 2), 0); var runner = new SimulationRunner(state, run % 2 == 0 ? a : b, run % 2 == 1 ? a : b); while (!state.GameHasEnded) { runner.PerformNextStep(); } Console.WriteLine(state.WinningPlayer); }
private void PopulateObservation(Observation res, SimulationState sim) { res.ObservationForNextMove = new float[PlayerObservations + LookAheadPieceAmount * PieceFields]; res.ObservationForNextMove[0] = sim.PlayerButtonIncome[0] / ButtonIncomeScale; res.ObservationForNextMove[1] = sim.PlayerButtonIncome[1] / ButtonIncomeScale; res.ObservationForNextMove[2] = sim.PlayerButtonAmount[0] / ButtonAmountScale; res.ObservationForNextMove[3] = sim.PlayerButtonAmount[1] / ButtonAmountScale; res.ObservationForNextMove[4] = sim.PlayerBoardUsedLocationsCount[0] / UsedLocationsScale; res.ObservationForNextMove[5] = sim.PlayerBoardUsedLocationsCount[1] / UsedLocationsScale; res.ObservationForNextMove[6] = sim.PlayerPosition[0] / PlayerPositionScale; res.ObservationForNextMove[7] = sim.PlayerPosition[1] / PlayerPositionScale; res.ObservationForNextMove[8] = SimulationHelpers.ButtonIncomeAmountAfterPosition(sim.PlayerPosition[0]) / IncomesRemainingScale; res.ObservationForNextMove[9] = SimulationHelpers.ButtonIncomeAmountAfterPosition(sim.PlayerPosition[1]) / IncomesRemainingScale; int baseIndex = PlayerObservations; for (var i = 0; i < LookAheadPieceAmount; i++) { var piece = PatchworkSim.AI.Helpers.GetNextPiece(sim, i); var b = baseIndex + (i * PieceFields); res.ObservationForNextMove[b + 0] = piece.ButtonCost / ButtonAmountScale; res.ObservationForNextMove[b + 1] = piece.ButtonsIncome / ButtonIncomeScale; res.ObservationForNextMove[b + 2] = piece.TotalUsedLocations / UsedLocationsScale; } }
public void Run() { Evaluate(); var req = new TrainRequest(); for (var g = 0; g < 1000; g++) { var timer = Stopwatch.StartNew(); //Parallel.For(0, 8, (i) => for (var i = 0; i < 8; i++) { Console.WriteLine("Starting game " + i); var p0Samples = new List <TrainSample>(); var p1Samples = new List <TrainSample>(); var state = new SimulationState(SimulationHelpers.GetRandomPieces(), 0); state.Fidelity = SimulationFidelity.NoPiecePlacing; while (!state.GameHasEnded) { var sampleSet = state.ActivePlayer == 0 ? p0Samples : p1Samples; var sample = _ai.MakeMoveWithResult(state); sampleSet.Add(sample); } //Set the winner on the samples from the winner var winningSamples = state.WinningPlayer == 0 ? p0Samples : p1Samples; for (var j = 0; j < winningSamples.Count; j++) { winningSamples[j].IsWin = true; } //TODO: AddRange is terrible performance lock (req) { req.Samples.AddRange(p0Samples); req.Samples.AddRange(p1Samples); } } //); timer.Stop(); Console.WriteLine("Took " + timer.ElapsedMilliseconds); //if (req.Samples.Count >= 256) { Console.WriteLine("Training " + req.Samples.Count); _client.Train(req); req.Samples.Clear(); Evaluate(); } } Console.WriteLine("Hi"); Console.ReadLine(); }
protected override double CalculateValue(SimulationState state, int pieceIndex, PieceDefinition piece) { var value = piece.TotalUsedLocations * 2 - piece.ButtonCost - piece.TimeCost * _timeCostValue; value += SimulationHelpers.ButtonIncomeAmountAfterPosition(state.PlayerPosition[state.ActivePlayer]) * piece.ButtonsIncome; //TODO: We should value gaining buttons more near the start of the game because they let us buy more, not sure if we need to put some extra stuff in for that return(value); }
public void BuyFirstPossibleMoveMakerNoPiecePlacing() { var state = new SimulationState(SimulationHelpers.GetRandomPieces(3), 0); state.Fidelity = SimulationFidelity.NoPiecePlacing; var runner = new SimulationRunner(state, new PlayerDecisionMaker(BuyFirstPossibleMoveMaker.Instance, null), new PlayerDecisionMaker(BuyFirstPossibleMoveMaker.Instance, null)); while (!state.GameHasEnded) { runner.PerformNextStep(); } //Check someone bought something Assert.True(state.Pieces.Count < PieceDefinition.AllPieceDefinitions.Length); }
internal (SimulationState state, int gameId) CreateSimulation(int?randomSeed, int mctsIterations) { lock (this) { var id = ++_nextSim; var state = new SimulationState(SimulationHelpers.GetRandomPieces(randomSeed), 0); state.Fidelity = SimulationFidelity.NoPiecePlacing; var opp = new MoveOnlyMonteCarloTreeSearchMoveMaker(mctsIterations); _simulations[id] = state; _opponents[id] = opp; return(state, id); } }
public void BuyFirstPossibleMoveMakerPlacing() { var state = new SimulationState(SimulationHelpers.GetRandomPieces(1), 0); var runner = new SimulationRunner(state, new PlayerDecisionMaker(BuyFirstPossibleMoveMaker.Instance, PlacementMaker.FirstPossibleInstance), new PlayerDecisionMaker(BuyFirstPossibleMoveMaker.Instance, PlacementMaker.FirstPossibleInstance)); while (!state.GameHasEnded) { runner.PerformNextStep(); } //Check someone bought something Assert.True(state.Pieces.Count < PieceDefinition.AllPieceDefinitions.Length); Assert.Equal(11, state.PlayerButtonIncome[0]); Assert.Equal(37, state.PlayerButtonAmount[0]); }
private void TestStrategy(IPlacementStrategy strategy, int expectPiecesPlaced) { var pieces = new PieceCollection(); pieces.Populate(SimulationHelpers.GetRandomPieces(1)); int placed = 0; var board = new BoardState(); for (var i = 0; i < pieces.Count; i++) { if (strategy.TryPlacePiece(board, PieceDefinition.AllPieceDefinitions[pieces[i]], in pieces, i + 1, out var bitmap, out var x, out var y)) { placed++; board.Place(bitmap, x, y); }
static void TestPlacementOnly() { var stopwatch = Stopwatch.StartNew(); var strategies = new IPlacementStrategy[] { //FirstPossiblePlacementStrategy.Instance, //SimpleClosestToWallAndCornerStrategy.Instance, //ClosestToCornerLeastHolesTieBreakerPlacementStrategy.Instance, //NextToPieceEdgeLeastHolesTieBreakerPlacementStrategy.Instance, //TightPlacementStrategy.InstanceDoubler, //TightPlacementStrategy.InstanceIncrement, ExhaustiveMostFuturePlacementsPlacementStrategy.Instance1_1, ExhaustiveMostFuturePlacementsPlacementStrategy.Instance1_6, new BestEvaluatorStrategy(TuneablePattern2x2BoardEvaluator.HandTuned), new BestEvaluatorStrategy(TuneablePattern2x2BoardEvaluator.Tuning1), }; //foreach (var strategy in strategies) Parallel.ForEach(strategies, (strategy) => { //var rand = new Random(0); int totalPlaced = 0; for (var i = 0; i < 100; i++) { var pieces = new PieceCollection(); pieces.Populate(SimulationHelpers.GetRandomPieces(i)); int index = 0; int placed = 0; var board = new BoardState(); while (true) { var piece = pieces[index]; pieces.RemoveAt(index); index = index % pieces.Count; if (strategy.TryPlacePiece(board, PieceDefinition.AllPieceDefinitions[piece], in pieces, index, out var bitmap, out var x, out var y)) { placed++; board.Place(bitmap, x, y); //Advance to a random one in the next 6 pieces (TODO: Would be good to bias this towards 1-3 as these are more likely) //index = (index + rand.Next(0, 6)) % pieces.Count; }
public void GetRandomPiecesPlaces0AtEnd() { for (var i = 0; i < 1000; i++) { var pieces = SimulationHelpers.GetRandomPieces(i); //0 at end Assert.Equal(0, pieces[pieces.Count - 1]); //Array contains all pieces pieces = pieces.OrderBy(p => p).ToList(); for (var j = 0; j < pieces.Count; j++) { Assert.Equal(j, pieces[j]); } } }
public void AdvanceToEndPlacingSinglePatches() { var state = new SimulationState(SimulationHelpers.GetRandomPieces(), 0); state.Fidelity = SimulationFidelity.FullSimulation; var runner = new SimulationRunner(state, new PlayerDecisionMaker(AlwaysAdvanceMoveMaker.Instance, PlacementMaker.FirstPossibleInstance), new PlayerDecisionMaker(AlwaysAdvanceMoveMaker.Instance, PlacementMaker.FirstPossibleInstance)); while (!state.GameHasEnded) { runner.PerformNextStep(); } //Both players should be at the end Assert.Equal(SimulationState.EndLocation, state.PlayerPosition[0]); Assert.Equal(SimulationState.EndLocation, state.PlayerPosition[1]); //Game should have ended Assert.True(state.GameHasEnded); //Players should have one button for each place they moved Assert.Equal(SimulationState.EndLocation + SimulationState.PlayerStartingButtons, state.PlayerButtonAmount[0]); Assert.Equal(SimulationState.EndLocation + SimulationState.PlayerStartingButtons, state.PlayerButtonAmount[1]); //non-starting player should have won by collecting all of the leather patches (https://boardgamegeek.com/thread/1703957/how-break-stalemate) Assert.Equal(1, state.WinningPlayer); //Check the ending points are correct Assert.Equal(SimulationState.EndLocation - BoardState.Width * BoardState.Height * 2 + SimulationState.PlayerStartingButtons, state.CalculatePlayerEndGameWorth(0)); Assert.Equal(SimulationState.EndLocation - BoardState.Width * BoardState.Height * 2 + SimulationState.PlayerStartingButtons + 2 * SimulationState.LeatherPatches.Length, state.CalculatePlayerEndGameWorth(1)); //Check the pieces are on their board int sum = 0; for (var x = 0; x < BoardState.Width; x++) { for (var y = 0; y < BoardState.Height; y++) { if (state.PlayerBoardState[1][x, y]) { sum++; } } } Assert.Equal(SimulationState.LeatherPatches.Length, sum); }
private void Evaluate() { Console.WriteLine($"Evaluating vs " + _opp.Name); var players = new IMoveDecisionMaker[] { _ai, _opp }; //Play X games (even amount), each player gets to start on each of the boards once for (var game = 0; game < 4; game++) { var state = new SimulationState(SimulationHelpers.GetRandomPieces(game / 2), 0); state.Fidelity = SimulationFidelity.NoPiecePlacing; state.ActivePlayer = game % 2; while (!state.GameHasEnded) { players[state.ActivePlayer].MakeMove(state); } Console.WriteLine($"Game {game}. Winner {state.WinningPlayer}. Scores: {state.CalculatePlayerEndGameWorth(0)} / {state.CalculatePlayerEndGameWorth(1)}"); } }
private void PopulateObservation(Observation res, SimulationState sim) { res.ObservationForNextMove.Add(sim.PlayerButtonIncome[0] / ButtonIncomeScale); res.ObservationForNextMove.Add(sim.PlayerButtonIncome[1] / ButtonIncomeScale); res.ObservationForNextMove.Add(sim.PlayerButtonAmount[0] / ButtonAmountScale); res.ObservationForNextMove.Add(sim.PlayerButtonAmount[1] / ButtonAmountScale); res.ObservationForNextMove.Add(sim.PlayerBoardUsedLocationsCount[0] / UsedLocationsScale); res.ObservationForNextMove.Add(sim.PlayerBoardUsedLocationsCount[1] / UsedLocationsScale); res.ObservationForNextMove.Add(sim.PlayerPosition[0] / PlayerPositionScale); res.ObservationForNextMove.Add(sim.PlayerPosition[1] / PlayerPositionScale); res.ObservationForNextMove.Add(SimulationHelpers.ButtonIncomeAmountAfterPosition(sim.PlayerPosition[0]) / IncomesRemainingScale); res.ObservationForNextMove.Add(SimulationHelpers.ButtonIncomeAmountAfterPosition(sim.PlayerPosition[1]) / IncomesRemainingScale); for (var i = 0; i < LookAheadPieceAmount; i++) { var piece = PatchworkSim.AI.Helpers.GetNextPiece(sim, i); res.ObservationForNextMove.Add(piece.ButtonCost / ButtonAmountScale); res.ObservationForNextMove.Add(piece.ButtonsIncome / ButtonIncomeScale); res.ObservationForNextMove.Add(piece.TotalUsedLocations / UsedLocationsScale); } }
private float CalculateValue(SimulationState state, int player) { return(state.PlayerBoardUsedLocationsCount[player] * 2 / UsedLocationsScale + state.PlayerButtonAmount[player] / ButtonAmountScale + state.PlayerButtonIncome[player] * SimulationHelpers.ButtonIncomeAmountAfterPosition(state.PlayerPosition[player]) / ButtonIncomeScale); }
public void ButtonIncomeAfter() { Assert.Equal(SimulationState.ButtonIncomeMarkers.Length, SimulationHelpers.ButtonIncomeAmountAfterPosition(0)); Assert.Equal(SimulationState.ButtonIncomeMarkers.Length - 1, SimulationHelpers.ButtonIncomeAmountAfterPosition(SimulationState.ButtonIncomeMarkers[0])); Assert.Equal(0, SimulationHelpers.ButtonIncomeAmountAfterPosition(SimulationState.EndLocation)); }
static void TestFullAi() { var aiToTest = new Func <PlayerDecisionMaker>[] { //() => new PlayerDecisionMaker(new GreedyCardValueUtilityMoveMaker(-1), PlacementMaker.TightDoublerInstance), //() => new PlayerDecisionMaker(new GreedyCardValueUtilityMoveMaker(0), PlacementMaker.TightDoublerInstance), //() => new PlayerDecisionMaker(new GreedyCardValueUtilityMoveMaker(1), PlacementMaker.TightDoublerInstance), //() => new PlayerDecisionMaker(new GreedyCardValueUtilityMoveMaker(2), PlacementMaker.TightDoublerInstance), // //() => new PlayerDecisionMaker(new GreedyCardValueUtilityMoveMaker(-1), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), //() => new PlayerDecisionMaker(new GreedyCardValueUtilityMoveMaker(0), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), //() => new PlayerDecisionMaker(new GreedyCardValueUtilityMoveMaker(1), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), //() => new PlayerDecisionMaker(new GreedyCardValueUtilityMoveMaker(2), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), //() => new PlayerDecisionMaker(new QuickRandomSearchMoveMaker(6, 1000), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), //() => new PlayerDecisionMaker(new QuickRandomSearchMoveMaker(10, 2000), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), //() => new PlayerDecisionMaker(new QuickRandomSearchMoveMaker(20, 5000), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), //() => new PlayerDecisionMaker(new QuickRandomSearchMoveMaker(30, 10000), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), //() => new PlayerDecisionMaker(new MoveOnlyMonteCarloTreeSearchMoveMaker(1000, TuneableUtilityMoveMaker.Tuning1), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), () => new PlayerDecisionMaker(new MoveOnlyMonteCarloTreeSearchMoveMaker(10000, TuneableUtilityMoveMaker.Tuning1), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), () => new PlayerDecisionMaker(new MoveOnlyMonteCarloTreeSearchMoveMaker(10000, TuneableUtilityMoveMaker.Tuning1), new PlacementMaker(new BestEvaluatorStrategy(TuneablePattern2x2BoardEvaluator.Tuning1))), () => { var p = new PreplacerStrategy(new EvaluatorTreeSearchPreplacer(TuneablePattern2x2BoardEvaluator.Tuning1, 4, 4, true)); var m = new MoveOnlyMonteCarloTreeSearchWithPreplacerMoveMaker(10000, TuneableUtilityMoveMaker.Tuning1, p); return(new PlayerDecisionMaker(m, new PlacementMaker(p))); }, () => new PlayerDecisionMaker(new AlphaBetaMoveMaker(15, TuneableByBoardPositionUtilityCalculator.Tuning1), PlacementMaker.ExhaustiveMostFuturePlacementsInstance1_6), () => new PlayerDecisionMaker(new AlphaBetaMoveMaker(15, TuneableByBoardPositionUtilityCalculator.Tuning1), new PlacementMaker(new BestEvaluatorStrategy(TuneablePattern2x2BoardEvaluator.Tuning1))), // //() => //{ // var p = new PreplacerStrategy(new EvaluatorTreeSearchPreplacer(new Pattern2x2BoardEvaluator(), 4, 4, true)); // var m = new MoveOnlyMinimaxWithAlphaBetaPruningWithPreplacerMoveMaker(13, TuneableByBoardPositionUtilityCalculator.Tuning1, p); // return new PlayerDecisionMaker(m, new PlacementMaker(p)); //}, () => { var p = new PreplacerStrategy(new EvaluatorTreeSearchPreplacer(TuneablePattern2x2BoardEvaluator.Tuning1, 4, 4, true)); var m = new MoveOnlyAlphaBetaWithPreplacerMoveMaker(15, TuneableByBoardPositionUtilityCalculator.Tuning1, p); return(new PlayerDecisionMaker(m, new PlacementMaker(p))); }, }; const int TotalRuns = 500; const bool enableConsoleLogging = false; //TODO: Play each AI against each other AI 100 times and print a table of results var totalWins = new int[aiToTest.Length, aiToTest.Length]; var totalTimeTaken = new long[aiToTest.Length]; Console.WriteLine($"Running {aiToTest.Length * (aiToTest.Length - 1) / 2} * {TotalRuns} Games"); int gameNumber = 0; for (var a = 0; a < aiToTest.Length; a++) { for (var b = a; b < aiToTest.Length; b++) { if (a == b) { continue; } long aiATime = 0; long aiBTime = 0; var aiA = aiToTest[a]; var aiB = aiToTest[b]; Console.WriteLine($"{++gameNumber} {aiA().Name} vs {aiB().Name}"); Parallel.For(0, TotalRuns, new ParallelOptions { MaxDegreeOfParallelism = 6 }, (run) => //for (var run = 0; run < TotalRuns; run++) { var state = new SimulationState(SimulationHelpers.GetRandomPieces(run / 2), 0); ConsoleLogger logger = null; if (enableConsoleLogging) { state.Logger = logger = new ConsoleLogger(state); } //state.Fidelity = SimulationFidelity.NoPiecePlacing; //Let each Ai have half of the goes first and half second var runner = new SimulationRunner(state, run % 2 == 0 ? aiA() : aiB(), run % 2 == 1 ? aiA() : aiB()); while (!state.GameHasEnded) { runner.PerformNextStep(); if (logger != null) { logger.PrintBoards(true); } } var aWin = run % 2 == state.WinningPlayer; lock (totalWins) { if (aWin) { totalWins[a, b]++; } else { totalWins[b, a]++; } } Interlocked.Add(ref aiATime, runner.Stopwatches[run % 2].ElapsedMilliseconds); Interlocked.Add(ref aiBTime, runner.Stopwatches[(run + 1) % 2].ElapsedMilliseconds); //Console.WriteLine(aWin); } ); Console.WriteLine(totalWins[a, b]); totalTimeTaken[a] += aiATime; totalTimeTaken[b] += aiBTime; } } //Calculate the total points for each var total = Enumerable.Range(0, aiToTest.Length).Select(ai => Enumerable.Range(0, aiToTest.Length).Sum(opponent => totalWins[ai, opponent])).ToArray(); //Dump a CSV with the results var filename = "result_" + DateTimeOffset.Now.Ticks + ".csv"; var res = new List <string>(); res.Add("," + string.Join(", ", aiToTest.Select(ai => ai().Name)) + ",Win%,Rank,Total Time"); for (var a = 0; a < aiToTest.Length; a++) { var line = aiToTest[a]().Name; for (var b = 0; b < aiToTest.Length; b++) { if (a == b) { line += ","; } else { line += "," + totalWins[a, b]; } } //Win%, rank, time line += "," + (total[a] / (float)(aiToTest.Length - 1) * 100 / TotalRuns).ToString("0.0"); line += "," + (aiToTest.Length - total.Count(c => c < total[a])); line += "," + totalTimeTaken[a]; res.Add(line); } Console.WriteLine("Saving results as " + filename); File.WriteAllLines(filename, res); Process.Start(filename); }
public static int EstimateEndgameValueUnbiased(SimulationState state, int player) { //space used * 2 + buttons + income * incomes remaining return(state.PlayerBoardUsedLocationsCount[player] * 2 + state.PlayerButtonAmount[player] + state.PlayerButtonIncome[player] * SimulationHelpers.ButtonIncomeAmountAfterPosition(state.PlayerPosition[player])); }