/// <summary> /// Finds all the possible move sequences for the current player in a given match. /// </summary> /// <param name="match">Match to check against</param> /// <returns>A list of the possible move sequences</returns> public static List <MoveSequence> GenerateAllSequences(Match match) { List <Move> validMoves = GameLogic.GetValidMoves(match); List <MoveSequence> allSequences = new List <MoveSequence>(); foreach (Move possibleMove in validMoves) { Match dummyMatch = match.Clone(); switch (dummyMatch.board.ApplyMove(possibleMove)) { case MoveType.Jump: case MoveType.Capture: List <MoveSequence> possibleSequences = new List <MoveSequence>(); ExpandMoveSequences(new MoveSequence(), possibleMove, match.Clone(), possibleSequences); allSequences.AddRange(possibleSequences); break; case MoveType.Normal: MoveSequence basic = new MoveSequence(); basic.AddMove(possibleMove); allSequences.Add(basic); break; } } return(allSequences); }
/// <summary> /// Finds the best position(s) to place new pieces at. /// </summary> /// <returns>An array with the best positions to place the new pieces at</returns> /// <remarks>Depending on how many unplaced pieces the player has, the returned array may contain one or two elements</remarks> public Position[] SpawnPieces() { List <Position[]> bestPositionSets = new List <Position[]>(); int bestScore = int.MinValue; if (match.unplacedPieces == 2) { foreach (Position freeSlot1 in match.freeSlots) { foreach (Position freeSlot2 in match.freeSlots) { if (freeSlot1.Equals(freeSlot2)) { continue; } Match dummyMatch = match.Clone(); // Creates a copy of the match where each move can be simulated dummyMatch.SpawnPiece(freeSlot1); dummyMatch.SpawnPiece(freeSlot2); int newScore = Minimax(dummyMatch, false, int.MinValue, int.MaxValue, 2) + BonusEvaluation(dummyMatch, 2); if (newScore < bestScore) { continue; } Position[] positionSet = new Position[2]; positionSet[0] = freeSlot1; positionSet[1] = freeSlot2; if (newScore > bestScore) { bestScore = newScore; bestPositionSets.Clear(); } bestPositionSets.Add(positionSet); } } } else { foreach (Position freeSlot in match.freeSlots) { Match dummyMatch = match.Clone(); // Creates a copy of the match where each move can be simulated dummyMatch.SpawnPiece(freeSlot); int newScore = Minimax(dummyMatch, false, int.MinValue, int.MaxValue, 2) + BonusEvaluation(dummyMatch, 2); if (newScore < bestScore) { continue; } Position[] positionSet = new Position[1]; positionSet[0] = freeSlot; if (newScore > bestScore) { bestScore = newScore; bestPositionSets.Clear(); } bestPositionSets.Add(positionSet); } } return(bestPositionSets[new System.Random().Next(bestPositionSets.Count)]); }
static void Main(string[] args) { Console.WriteLine("Final Example"); //Inflate match information Match match = new Match(); Console.WriteLine(match.Print()); //Clone match information - Don't instantiate again Console.ForegroundColor = ConsoleColor.Green; Match clone = match.Clone() as Match; Console.WriteLine(clone.Print()); Console.ForegroundColor = ConsoleColor.Red; BasketEndPoint basketApi = new BasketEndPoint(); //Get basket match information to Api //API returns us a object with private constructors! We can't instantiate the class! BasketMatch basketMatch = basketApi.Get(); Console.WriteLine(basketMatch.Print()); //INTERNAL Constructors //BasketMatch.BasketMatch()' is inaccessible due to its protection level! //var newBasket = new BasketMatch(); //The class can be cloned BasketMatch cloneBasketMatch = basketMatch.Clone() as BasketMatch; Console.WriteLine(cloneBasketMatch.Print()); }
internal Wildcard(Match.MatchOptions options, Match.MatchParameter parameter) { _Options = options.Clone(); _Options.SynonymOutput = false; _Options.WildcardOutput = false; _Parameter = parameter.Clone(); }
/// <summary> /// Expands a move to form every possible capture or jump sequence from it. /// </summary> /// <param name="expandingSequence">Move sequence being expanded</param> /// <param name="move">Move to expand from</param> /// <param name="match">Match to check against</param> /// <param name="moveSeqList">List of the expanded move sequences</param> /// <remarks> /// The <paramref name="moveSeqList"/> should be initialized as a new object before the initial function call. /// </remarks> private static void ExpandMoveSequences(MoveSequence expandingSequence, Move move, Match match, List <MoveSequence> moveSeqList) { expandingSequence.AddMove(move); match.ExecuteMove(move); List <Position> nextDestinies = move.type == MoveType.Capture? GameLogic.GetCaptureMoveDestinies(match, move.endPosition) : GameLogic.GetJumpMoveDestinies(match, move.endPosition); if (nextDestinies.Count == 0) { moveSeqList.Add(expandingSequence); return; } List <Move> nextMoves = ToMovesList(move.endPosition, nextDestinies, move.type == MoveType.Capture? MoveType.Capture : MoveType.Jump); foreach (Move nextMove in nextMoves) { Match dummyMatch = match.Clone(); dummyMatch.ExecuteMove(nextMove); MoveSequence moveSeqTemp = expandingSequence.Clone(); ExpandMoveSequences(moveSeqTemp, nextMove, match.Clone(), moveSeqList); } }
/// <summary> /// Implementation of the Minimax algorithm. /// </summary> /// <param name="match">Match being evaluated</param> /// <param name="maximizing">True if the algorithm is being applied to the maximizing player, false otherwise</param> /// <param name="alpha">Best value that the maximizer can guarantee</param> /// <param name="beta">Best value that the maximizer can guarantee</param> /// <param name="depth">Search depth</param> /// <returns>The expected evaluation for the initially input <paramref name="match"/> based on the Minimax algorithm</returns> private int Minimax(Match match, bool maximizing, int alpha, int beta, int depth) { if (match.isGameOver()) { return(maximizing? int.MaxValue : int.MinValue); } if (depth == 1) // Reach of depth limit { return(EvaluateState(match, 2)); } List <MoveSequence> nextSequences = GenerateAllSequences(match); // Generates the possible moves from the received game state int bestScore = maximizing? int.MinValue : int.MaxValue; foreach (MoveSequence moveSequence in nextSequences) { Match dummyMatch = match.Clone(); // Creates a copy of the match where each move can be simulated dummyMatch.board.ApplyMoveSequence(moveSequence); // Simulates the move or sequence of moves dummyMatch.ChangeTurn(); int newScore = Minimax(dummyMatch, !maximizing, alpha, beta, depth - 1); // Calls minimax with the new game state if (maximizing) { bestScore = Math.Max(bestScore, newScore); alpha = Math.Max(alpha, newScore); } else { bestScore = Math.Min(bestScore, newScore); beta = Math.Min(beta, newScore); } if (beta <= alpha) // Alpha-beta pruning { break; } } return(bestScore); }
public Match UpdateScoreReport(MatchReportDTO matchReportDto) { //Find the corresponding match var match = _matchRepository.FindById(matchReportDto.matchId); var oldMatch = Match.Clone(match); var draw = _drawRepository.FindById(match.DrawId); //Format the string and check for errors var p1Points = new List <int>(); var p2Points = new List <int>(); var gameScores = matchReportDto.score.Split(" "); int validGameCount = 0; for (int i = 0; i < gameScores.Length; i++) { try { var pointStrings = gameScores[i].Split("/"); var p1 = Int32.Parse(pointStrings[0]); var p2 = Int32.Parse(pointStrings[1]); p1Points.Add(p1); p2Points.Add(p2); validGameCount++; } catch (Exception e) { //When formatting goes wrong - break the for loop - this might be when we encounter "__/__" break; } } //Check that number of games is as expected var maxGames = draw.Games; var minGames = (1 + maxGames) / 2; if (validGameCount > maxGames) //Too many games { throw new TournamentSoftwareException( $"{validGameCount} valid games in score-string {1}, but a maximum of {maxGames} allowed."); } //Too few games if (validGameCount < minGames) { throw new TournamentSoftwareException( $"{validGameCount} valid games in score-string {matchReportDto.score}," + $" but at least {minGames} were required in draw with up to {draw.Games} games."); } //Check that points correspond to expected values for (int i = 0; i < validGameCount; i++) { var overMax = p1Points[i] > draw.Points || p2Points[i] > draw.Points; if (draw.TieBreaks) //If tie breaks are allowed //There can be too many points, but only if there is excactly 2 points between scores { if (overMax && Math.Abs(p1Points[i] - p2Points[i]) != 2) { throw new TournamentSoftwareException( $"Score in score-string {matchReportDto.score} does not follow tie break rules." + $"A max of {draw.Points} is allowed, unless it is a tie break."); } } else //If tie breaks are not allowed - no score must be greater than max { if (overMax) { throw new TournamentSoftwareException( $"Too high point score in score-string {matchReportDto.score}." + $"A max of {draw.Points} is allowed, since tie breaks are disallowed."); } } } //Find the winner int p1Games = 0, p2Games = 0, p1PointsTotal = 0, p2PointsTotal = 0; for (int i = 0; i < validGameCount; i++) { p1PointsTotal += p1Points[i]; p2PointsTotal += p2Points[i]; if (p1Points[i] > p2Points[i]) { p1Games++; } else if (p1Points[i] < p2Points[i]) { p2Games++; } else { throw new TournamentSoftwareException( $"Game with equal score in score-string {matchReportDto.score}"); } } if (p1Games > p2Games) { match.P1Won = true; } else if (p1Games < p2Games) { match.P1Won = false; } else { //Winner is decided by who won the most points match.P1Won = p1PointsTotal > p2PointsTotal; } //Set variables match.P1PointsArray = p1Points.ToArray(); match.P2PointsArray = p2Points.ToArray(); match.P1Games = p1Games; match.P2Games = p2Games; match.Status = Status.FINISHED; //Update the match using existing Update method and return return(Update(match, oldMatch)); }