/// <summary>
        /// Recursively search for the best move for the given board
        /// </summary>
        /// <param name="depth">How many recursive iterations you want to take(each iteration is 1 turn of 1 of the players, so steps ahead is depth/2</param>
        /// <param name="tiles">The Tile to iterate over</param>
        /// <param name="initialSequence">Initial sequence of moves, which is used as state to know what sequence </param>
        /// <param name="priority"></param>
        /// <param name="max"></param>
        /// <param name="currentPlayer"></param>
        /// <param name="enemyPlayer"></param>
        /// <returns></returns>
        public MinimaxResult GetBestTurn(BoardTileCollection tiles, IPlayer currentPlayer)
        {
#if DEBUG
            this._amountOfCheckups = 0;
#endif
            IPlayer enemyPlayer = this.Game.GetEnemyPlayer(currentPlayer);
            var     results     = new List <MinimaxResult>();

            //get all possible initial moves for the player
            var checkerMovePairs = currentPlayer.GetAllPossibleMoves(Game, enemyPlayer, tiles);

            Parallel.ForEach(checkerMovePairs, (pair, loopState) => {
                List <MoveSequence> possibleSequences = null;
                Turn turn = pair.Value;
                foreach (var move in turn.Moves)                  //for each initial move of the checker
                {
                    if (move is AttackMove attMove)
                    {
                        //all possible followup move sequences
                        possibleSequences = GetPossibleMoveSequences(turn, attMove);
                    }
                    else if (move is WalkMove)
                    {
                        possibleSequences = new List <MoveSequence> {
                            new MoveSequence(turn, new List <Move> {
                                move
                            })
                        };
                    }
                    //in case theres only 1 possible move, we dont need to calculate anything, so cut of.
                    if (checkerMovePairs.Count == 1 && possibleSequences.Count == 1)
                    {
                        var seq = possibleSequences.First();
                        results.Add(new MinimaxResult(seq.Moves, seq.Turn, 0));
                        loopState.Break();
                    }

                    foreach (var sequence in possibleSequences)
                    {
                        BoardTileCollection clonedTiles = (BoardTileCollection)tiles.Clone();
                        float prio = sequence.Moves[0].Priority;
                        foreach (var mov in sequence.Moves)
                        {
                            Board.TakeMove(clonedTiles, mov);
                        }
                        //now we need to call search the move tree for the value of the sequence.
                        var sequenceValue = GetValueOfMoveSequence(this.MaxDepth - 1, clonedTiles, prio, enemyPlayer, currentPlayer, false);

                        var result = new MinimaxResult(sequence.Moves, sequence.Turn, sequenceValue);
                        lock (_resultAddLock)
                            results.Add(result);
                    }
                }
            });

#if DEBUG
            Console.WriteLine($"amount of checkups: {this._amountOfCheckups}");
#endif
            return(ChooseBestResult(results));
        }
示例#2
0
        protected float GetValueOfMoveSequence(int depth, BoardTileCollection tiles, float priority, IPlayer currentPlayer, IPlayer enemyPlayer, int alpha, int beta)
        {
            if (depth == 0)
            {
                return(priority);
            }

            var checkerMovePairs = currentPlayer.GetAllPossibleMoves(Game, enemyPlayer, tiles);

            //This paralelisation is increasing the performance quite a lot, but since its called recursively it's maybe not smart to run it in from the start to the end?
            //I expect this would create quite a bit of overhead by creating millions of tasks.
            Parallel.ForEach(checkerMovePairs, (pair) => {
#if DEBUG
                lock (this._amountOfCheckupsLock)
                    this._amountOfCheckups++;
#endif
                List <MoveSequence> possibleSequences = null;
                Turn turn = pair.Value;
                foreach (var move in turn.Moves)
                {
                    if (move is AttackMove attMove)
                    {
                        possibleSequences = GetPossibleMoveSequences(turn, attMove);
                    }
                    else
                    {
                        possibleSequences = new List <MoveSequence> {
                            new MoveSequence(turn, new List <Move> {
                                move
                            })
                        };
                    }

                    foreach (var sequence in possibleSequences)
                    {
                        BoardTileCollection clonedTiles = (BoardTileCollection)tiles.Clone();
                        int prio = sequence.Moves[0].Priority;
                        foreach (var mov in sequence.Moves)
                        {
                            Board.TakeMove(clonedTiles, mov);
                        }

                        priority -= prio;
                        //now we need to call the same method from the enemy perception again
                        // recursively collect all sequences for the followup moves
                        var furtherMovesValue = GetValueOfMoveSequence(depth - 1, clonedTiles, -priority, enemyPlayer, currentPlayer, -beta, -alpha);
                    }
                }
            });

            return(priority);
        }
示例#3
0
        /// <summary>
        /// Recursively search for the best move for the given board
        /// </summary>
        /// <param name="depth">How many recursive iterations you want to take(each iteration is 1 turn of 1 of the players, so steps ahead is depth/2</param>
        /// <param name="tiles">The Tile to iterate over</param>
        /// <param name="initialSequence">Initial sequence of moves, which is used as state to know what sequence </param>
        /// <param name="priority"></param>
        /// <param name="max"></param>
        /// <param name="currentPlayer"></param>
        /// <param name="enemyPlayer"></param>
        /// <returns></returns>
        public MinimaxResult GetBestTurn(BoardTileCollection tiles, IPlayer currentPlayer)
        {
            IPlayer enemyPlayer = this.Game.GetEnemyPlayer(currentPlayer);
            var     results     = new List <MinimaxResult>();

            //get all possible initial moves for the player
            var checkerMovePairs = currentPlayer.GetAllPossibleMoves(Game, enemyPlayer, tiles);

            //loop variables

            Parallel.ForEach(checkerMovePairs, (pair) => {
                //foreach(var pair in checkerMovePairs) { //for each checker
                List <MoveSequence> possibleSequences = null;
                Turn turn = pair.Value;
                foreach (var move in turn.Moves)                  //for each initial move of the checker
                {
                    if (move is AttackMove attMove)
                    {
                        //all possible followup move sequences
                        possibleSequences = GetPossibleMoveSequences(turn, attMove);
                    }
                    else if (move is WalkMove)
                    {
                        possibleSequences = new List <MoveSequence> {
                            new MoveSequence(turn, new List <Move> {
                                move
                            })
                        };
                    }
                    //Parallel.ForEach(possibleSequences, (sequence) => { // Not useful, its max 2 moves
                    foreach (var sequence in possibleSequences)
                    {
                        BoardTileCollection clonedTiles = (BoardTileCollection)tiles.Clone();
                        float prio = sequence.Moves[0].Priority;
                        foreach (var mov in sequence.Moves)
                        {
                            Board.TakeMove(clonedTiles, mov);
                        }

                        //now we need to call the same method from the enemy perception again
                        var sequenceValue = GetBestMoveSequence(this.MaxDepth, clonedTiles, prio, enemyPlayer, currentPlayer);

                        var result = new MinimaxResult(sequence.Moves, sequence.Turn, sequenceValue);
                        results.Add(result);
                    }
                    //});
                }
            });
            //}
            return(ChooseBestResult(results));
        }
示例#4
0
        protected float GetBestMoveSequence(int depth, BoardTileCollection tiles, float priority, IPlayer currentPlayer, IPlayer enemyPlayer, int alpha, int beta)
        {
            if (depth == 0)
            {
                return(priority);
            }

            var checkerMovePairs = currentPlayer.GetAllPossibleMoves(Game, enemyPlayer, tiles);
            //Loop variables
            //stores all the generated move sequences, instantiated outside loop to prevent unneeded creating of variables
            List <MoveSequence> possibleSequences = null;

            foreach (var pair in checkerMovePairs)
            {
                Turn turn = pair.Value;
                foreach (var move in turn.Moves)
                {
                    if (move is AttackMove attMove)
                    {
                        possibleSequences = GetPossibleMoveSequences(turn, attMove);
                    }
                    else
                    {
                        possibleSequences = new List <MoveSequence> {
                            new MoveSequence(turn, new List <Move> {
                                move
                            })
                        };
                    }

                    foreach (var sequence in possibleSequences)
                    {
                        BoardTileCollection clonedTiles = (BoardTileCollection)tiles.Clone();
                        int prio = sequence.Moves[0].Priority;
                        foreach (var mov in sequence.Moves)
                        {
                            Board.TakeMove(clonedTiles, mov);
                        }

                        priority += prio;
                        //now we need to call the same method from the enemy perception again
                        // recursively collect all sequences for the followup moves
                        var furtherMovesValue = GetBestMoveSequence(depth - 1, clonedTiles, -priority, enemyPlayer, currentPlayer, -beta, -alpha);
                    }
                }
            }

            return(priority);
        }
        public void GetCrossChecksForBoardTiles_CreateABoardWithWordBoingAndUsingBoingDawg_AssertThatCrossChecksAreCorrectBeforeAndAfterTransposition()
        {
            Board board = new(3, 7);

            board.PlaceCharTile(2, 2, 'B');
            board.PlaceCharTile(2, 3, 'O');
            board.PlaceCharTile(2, 4, 'I');
            board.PlaceCharTile(2, 5, 'N');
            board.PlaceCharTile(2, 6, 'G');

            BoardAnchorCollector boardAnchorCollector = new();
            BoardTileCollection  boardAnchors         = boardAnchorCollector.GetAnchors(board);

            BoardCrossCheckCollector boardCrossCheckCollector = new(board, UnitTestGlobals.BoingDawgWithAlphabet);
            Dictionary <BoardTile, HashSet <char> > crossChecksForNormalBoard = boardCrossCheckCollector.GetCrossChecksForBoardTiles(boardAnchors);

            Assert.IsTrue(crossChecksForNormalBoard.Count == 12);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(1, 2)].Count == 2);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(1, 3)].Count == 17);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(1, 4)].Count == 13);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(1, 5)].Count == 5);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(1, 6)].Count == 2);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(3, 2)].Count == 5);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(3, 3)].Count == 16);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(3, 4)].Count == 6);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(3, 5)].Count == 5);
            Assert.IsTrue(crossChecksForNormalBoard[board.GetBoardTileAtCoordinates(3, 6)].Count == 3);

            BoardTransposer transposer = new(board);

            transposer.TransposeBoard();

            Dictionary <BoardTile, HashSet <char> > crossChecksForTransposedBoard = boardCrossCheckCollector.GetCrossChecksForBoardTiles(boardAnchors);

            Assert.IsTrue(crossChecksForTransposedBoard[board.GetBoardTileAtCoordinates(1, 2)].Count == 0);
            Assert.IsTrue(crossChecksForTransposedBoard[board.GetBoardTileAtCoordinates(7, 2)].Count == 1);

            transposer.TransposeBoard();
            Assert.IsTrue(board.RowCount == 3);
            Assert.IsTrue(board.ColumnCount == 7);
            Assert.IsTrue(board.GetBoardTileAtCoordinates(2, 3).CharTile.Letter == 'O');
            Assert.IsTrue(board.GetBoardTileAtCoordinates(2, 3).X == 2);
            Assert.IsTrue(board.GetBoardTileAtCoordinates(2, 3).Y == 3);
        }
示例#6
0
 protected float GetValueOfMoveSequence(int depth, BoardTileCollection tiles, float priority, IPlayer currentPlayer, IPlayer enemyPlayer) =>
 GetValueOfMoveSequence(depth, tiles, priority, currentPlayer, enemyPlayer, int.MaxValue, int.MinValue);
示例#7
0
 public IEnumerable <IChecker> GetPlayerOwnedCheckers(BoardTileCollection tiles) => tiles.Where(t => t.Checker != null)
 .Select(t => t.Checker).Where(c => c.Owner == this.PlayerNumber);
        protected float GetValueOfMoveSequence(int depth, BoardTileCollection tiles, float priority, IPlayer currentPlayer, IPlayer enemyPlayer, bool max)
        {
#warning DE BUG dat ai niet goed kijkt komt denk ik omdat ik de checker niet copy aan het begin, dus de checker data (is king, enz.) kan op de ene plek al geupdatet worden, en in de andere tree vervolgens verkeert berekent. FIX DIT.
            //Exit condition
            if (depth == 0)
            {
                return(priority);
            }
            float bestPrio         = priority;
            var   checkerMovePairs = currentPlayer.GetAllPossibleMoves(Game, enemyPlayer, tiles);
            //This paralelisation is increasing the performance quite a lot, but since its called recursively it's maybe not smart to run it in from the start to the end?
            //I expect this would create quite a bit of overhead by creating millions of tasks.
            Parallel.ForEach(checkerMovePairs, (pair) => {
                #region DEBUG
#if DEBUG
                lock (this._amountOfCheckupsLock)
                    this._amountOfCheckups++;
#endif
                #endregion
                List <MoveSequence> possibleSequences;
                Turn turn = pair.Value;
                foreach (var move in turn.Moves)
                {
                    if (move is AttackMove attMove)
                    {
                        possibleSequences = GetPossibleMoveSequences(turn, attMove);
                    }
                    else
                    {
                        possibleSequences = new List <MoveSequence> {
                            new MoveSequence(turn, new List <Move> {
                                move
                            })
                        };
                    }
                    List <float> betterFoundPrios = new List <float>();
                    foreach (MoveSequence sequence in possibleSequences)
                    {
                        BoardTileCollection clonedTiles = (BoardTileCollection)tiles.Clone();
                        int prio = sequence.Moves[0].Priority;
                        foreach (var mov in sequence.Moves)
                        {
                            //TODO: atm it doesnt update checker to king when looking in the future. for this we probarly need to clone checker to.
                            Board.TakeMove(clonedTiles, mov);
                        }

                        float newPrio = max ?
                                        priority + prio
                                                        : priority - prio;

                        //now we need to call the same method from the enemy perception again
                        // recursively collect all sequences for the followup moves
                        var furtherMovesValue = GetValueOfMoveSequence(depth - 1, clonedTiles, newPrio, enemyPlayer, currentPlayer, !max);
                        //if(max) {
                        lock (_addSequenceLock) {
                            if (furtherMovesValue > bestPrio)
                            {
                                betterFoundPrios.Add(furtherMovesValue);
                            }
                        }

                        //}
                        //else {
                        //	lock(_addSequenceLock) {
                        //		if(furtherMovesValue < bestPrio) {
                        //			bestPrio = furtherMovesValue;
                        //		}
                        //	}
                        //}
                        //results.Add(furtherMovesValue);
                        //return furtherMovesValue;
                    }
                    //get the best of the fould prios
                    if (betterFoundPrios.Any())
                    {
                        lock (_addSequenceLock) {
                            bestPrio = betterFoundPrios.Max();
                        }
                    }
                }
            });

            return(bestPrio);
        }
示例#9
0
        /// <summary>
        /// Recursively search for the best move for the given board
        /// </summary>
        /// <param name="depth">How many recursive iterations you want to take(each iteration is 1 turn of 1 of the players, so steps ahead is depth/2</param>
        /// <param name="tiles">The Tile to iterate over</param>
        /// <param name="initialSequence">Initial sequence of moves, which is used as state to know what sequence </param>
        /// <param name="priority"></param>
        /// <param name="max"></param>
        /// <param name="currentPlayer"></param>
        /// <param name="enemyPlayer"></param>
        /// <returns></returns>
        public MinimaxResult GetBestTurn(BoardTileCollection tiles, IPlayer currentPlayer)
        {
            IPlayer enemyPlayer = this.Game.GetEnemyPlayer(currentPlayer);
            var     results     = new List <MinimaxResult>();

            //get all possible initial moves for the player
            var checkerMovePairs = currentPlayer.GetAllPossibleMoves(Game, enemyPlayer, tiles);
            //loop variables
            List <MoveSequence> possibleSequences = null;

            foreach (var pair in checkerMovePairs)              //for each checker
            {
                Turn turn = pair.Value;
                foreach (var move in turn.Moves)                  //for each initial move of the checker
                {
                    if (move is AttackMove attMove)
                    {
                        //all possible followup move sequences
                        possibleSequences = GetPossibleMoveSequences(turn, attMove);
                    }
                    else if (move is WalkMove)
                    {
                        possibleSequences = new List <MoveSequence> {
                            new MoveSequence(turn, new List <Move> {
                                move
                            })
                        };
                    }

                    foreach (var sequence in possibleSequences)
                    {
                        BoardTileCollection clonedTiles = (BoardTileCollection)tiles.Clone();
                        float prio = sequence.Moves[0].Priority;
                        foreach (var mov in sequence.Moves)
                        {
                            Board.TakeMove(clonedTiles, mov);
                        }

                        //now we need to call the same method from the enemy perception again
                        var sequenceValue = GetBestMoveSequence(this.MaxDepth, clonedTiles, prio, enemyPlayer, currentPlayer);

                        var result = new MinimaxResult(sequence.Moves, sequence.Turn, sequenceValue);
                        results.Add(result);
                    }
                }
            }

#if DEBUG
            var str = "";
            foreach (var result in results)
            {
                str += $"move: {result.Moves.First().StartLocation} => {result.Moves.Last().EndLocation}, value:{result.TurnValue}\n";
            }
            Console.WriteLine($"Turn: {this.Game.Turn}");
            Console.WriteLine(str);
#endif

            if (results.Count() == 0)
            {
                return(null);
            }
            IEnumerable <MinimaxResult> bestResults = ((this.MaxDepth & 0b1) == 0b1) ? results.Where(res => res.TurnValue == results.Max(r => r.TurnValue))
                                : results.Where(res => res.TurnValue == results.Min(r => r.TurnValue));

            var random       = new Random();
            var ind          = random.Next(0, bestResults.Count());
            var chosenResult = bestResults.ElementAtOrDefault(ind);
            return(chosenResult);
        }