Example #1
0
        public LocationType GetLocationAtCoord(CoordAbs c) => LocationsLayout.TryIndex(c.X, c.Y); // can return null

        /// <summary>
        /// Returns the players that are still in the game, checking each by the "active" requirements
        /// </summary>
        /// <param name="rules"></param>
        /// <param name="board"></param>
        /// <returns></returns>
        public List <Player> TerminalStateCheck(GameRules rules) // uses the custom TerminalStateFunction on each player
        {
            List <Player> activePlayers = new List <Player>(rules.PlayerOrder);

            foreach (Player p in rules.PlayerOrder)
            {
                // remove a player that no longer satisfies the "active" requirements
                var playerResult = rules.TerminalStateFunction(rules, this, p);
                if (playerResult == true) // instant win
                {
                    return new List <Player>()
                           {
                               p
                           }
                }
                ;
                else if (playerResult == false) // removed from game
                {
                    activePlayers.Remove(p);
                }
                else
                {
                    continue; // still in game if null
                }
            }
            return(activePlayers);
        }
Example #2
0
        public GameRules.MoveOutcomes applyMove(Move m, GameRules rules) // move must be legal
        {
            // apply the movement
            m.movingPiece.pos = m.ToCoord;
            this.PiecesLayout[m.FromCoord.X, m.FromCoord.Y] = null;
            this.PiecesLayout[m.ToCoord.X, m.ToCoord.Y]     = null;

            // using a single battle function allows for easier customization

            // if a battle took place at all, reveal the pieces
            if (m.movingPiece != null && m.opponentPiece != null)
            {
                m.movingPiece.turnRevealed = TurnNumber;
            }
            if (m.opponentPiece != null)
            {
                m.opponentPiece.turnRevealed = TurnNumber;
            }

            m.outcome = rules.BattleFunction(m.movingPiece, m.opponentPiece);

            switch (m.outcome)
            {
            case GameRules.MoveOutcomes.Move:
                this.PiecesLayout[m.ToCoord.X, m.ToCoord.Y] = m.movingPiece;
                break;

            case GameRules.MoveOutcomes.Win:
                this.PiecesLayout[m.ToCoord.X, m.ToCoord.Y] = m.movingPiece;

                // remove the opponent
                m.opponentPiece.pos = Piece.removedPos;
                PieceSet.Remove(m.opponentPiece);
                break;

            case GameRules.MoveOutcomes.Lose:
                this.PiecesLayout[m.ToCoord.X, m.ToCoord.Y] = m.opponentPiece;

                // remove our piece
                m.movingPiece.pos = Piece.removedPos;
                PieceSet.Remove(m.movingPiece);
                break;

            case GameRules.MoveOutcomes.Tie:
                // remove the opponent
                m.opponentPiece.pos = Piece.removedPos;
                PieceSet.Remove(m.opponentPiece);
                // remove our piece
                m.movingPiece.pos = Piece.removedPos;
                PieceSet.Remove(m.movingPiece);
                break;
            }

            return(m.outcome);
        }
Example #3
0
 public IEnumerable <Move> GetLegalMovesForPlayer(Player p, GameRules rules)
 {
     // todo: itd be nice to index pieces by player, but we dont persist players into projected board states
     foreach (Piece piece in this.PieceSet.Where(x => x.Owner == p))
     {
         foreach (Move m in piece.GetLegalMoves(this, rules))
         {
             yield return(m);
         }
     }
 }
Example #4
0
        public Game(Game othergame)
        {
            // TODO some data attributes may not be completely deep-cloned, such as players and logging settings
            CurrentBoard = new Board(othergame.CurrentBoard); // deep copy of the board
            //MoveSequence = new Dictionary<int, Move>(othergame.MoveSequence);


            rules = new GameRules(othergame.rules.Arsenals, new List <Player>(othergame.rules.PlayerOrder), null)
            {
                BattleFunction        = othergame.rules.BattleFunction,
                LoggingSettings       = othergame.rules.LoggingSettings,
                TerminalStateFunction = othergame.rules.TerminalStateFunction,
                MaxPhysicalTurns      = othergame.rules.MaxPhysicalTurns,
            };
        }
Example #5
0
        /// <summary>
        /// Wraps the piece type's check for winner and adds if the piece is revealed
        /// </summary>
        /// <param name="opponent"></param>
        /// <returns></returns>
        public GameRules.MoveOutcomes PredictWinner(Piece opponent, GameRules rules)
        {
            if (opponent == null)
            {
                return(GameRules.MoveOutcomes.Move);
            }

            if (opponent.IsRevealed)
            {
                return(rules.BattleFunction(this, opponent));
            }

            // TODO CHANGE winner determiniation function according to controller
            // according to minimax rules, if we dont know we should assume a loss
            return(GameRules.MoveOutcomes.Lose);
        }
Example #6
0
        public IEnumerable <Move> GetLegalMoves(Board currentBoard, GameRules rules)
        {
            foreach (CoordRel newCoord in GetPossibleMovement(currentBoard))
            {
                // generate the move and validate it
                Move m = new Move(this, newCoord, currentBoard, rules);

                if (m.Legal)
                {
                    if (rules.LoggingSettings.showEachPlayersPlanning)
                    {
                        Console.WriteLine("possible move: " + m.ToString());
                    }

                    yield return(m);
                }
            }
        }
Example #7
0
        public static Game tinyStratego()
        {
            LocationType _____Space = new LocationType();


            GameRules rules = new GameRules(

                _arsenal: new List <GameRules.Arsenal>()
            {
                // min, max, and start define the range of pieces a player can place to start the game
                new GameRules.Arsenal(0, 1, 1, staticPlayer1, pieceTypeBomb),
                new GameRules.Arsenal(0, 1, 1, staticPlayer1, pieceTypeMiner),
                new GameRules.Arsenal(0, 1, 1, staticPlayer1, pieceTypeFlag),

                new GameRules.Arsenal(0, 1, 1, staticPlayer2, pieceTypeBomb),
                new GameRules.Arsenal(0, 1, 1, staticPlayer2, pieceTypeMiner),
                new GameRules.Arsenal(0, 1, 1, staticPlayer2, pieceTypeFlag),
            },

                _players: new List <Player>()
            {
                staticPlayer1,
                staticPlayer2
            },

                _startingBoard: new Board()
            {
                Height          = 3,
                Width           = 3,
                LocationsLayout = new[, ]
                {
                    // x, y     so first row is actually the first column
                    { _____Space, _____Space, _____Space },
                    { _____Space, _____Space, _____Space },
                    { _____Space, _____Space, _____Space },
                },
                PiecesLayout = new Piece[3, 3],
                PieceSet     = new List <Piece>
                {
                    /*
                     * [      ,  -2  ,  -f ]
                     * [  +1  ,      ,  -1 ]
                     * [  +f  ,  +2  ,     ]
                     */
                    new Piece(0, 0, staticPlayer1, pieceTypeFlag),
                    new Piece(1, 0, staticPlayer1, pieceTypeMiner),
                    new Piece(0, 1, staticPlayer1, pieceTypeBomb),

                    new Piece(2, 1, staticPlayer2, pieceTypeBomb),
                    new Piece(1, 2, staticPlayer2, pieceTypeMiner),
                    new Piece(2, 2, staticPlayer2, pieceTypeFlag)
                }
            }
                )
            {
                // Function to determine if a player automatically wins (true), lost (false), or is still active in game (null)
                TerminalStateFunction = (theRules, board, player) =>
                {
                    bool?  result = null;
                    string reason = "unknown";

                    // over turn limit is a loss - used for turn planning
                    if (board.TurnNumber >= theRules.MaxPhysicalTurns)
                    {
                        reason = "Exceeded maximum turns";
                        result = false;
                    }

                    // game is over if player lost their flag
                    if (!board.PieceSet.Exists(x => x.Type == pieceTypeFlag &&
                                               x.pos != Piece.removedPos &&
                                               x.Owner == player))
                    {
                        reason = "Lost flag";
                        result = false;
                    }

                    // if we lost all our movable pieces
                    bool lostAllPieces = board.PieceSet.Where(x => x.Owner == player &&
                                                              x.Type.Movable)
                                         .All(x => x.pos == Piece.removedPos);
                    if (lostAllPieces)
                    {
                        reason = "Lost all mobile pieces";
                        result = false;
                    }

                    if (theRules.LoggingSettings.winLossReasons)
                    {
                        if (result == true)
                        {
                            Console.WriteLine(player.FriendlyName + " won ... " + reason);
                        }
                        if (result == false)
                        {
                            Console.WriteLine(player.FriendlyName + " lost ... " + reason);
                        }
                    }

                    return(result); // otherwise still in game
                },
                BattleFunction = FullBattleFunction,
            };


            Game game = new Game()
            {
                GameNumber   = 1,
                rules        = rules,
                CurrentBoard = new Board(rules.InitialBoard),
            };

            return(game);
        }
Example #8
0
        public static Game FullNewStratego(Player p1, Player p2)
        {
            LocationType _____Space = new LocationType();
            LocationType plyr1Space = new LocationType()
            {
                StarterPlace = p1,
            };
            LocationType plyr2Space = new LocationType()
            {
                StarterPlace = p2,
            };

            LocationType waterSpace = new LocationType()
            {
                Passable  = false,
                Standable = false,
            };

            int          pieceCount = 0;
            List <Piece> pieces     = new List <Piece>();

            #region Board Setup

            var Arsenal = new List <GameRules.Arsenal>()
            {
                // https://en.wikipedia.org/wiki/Stratego#Pieces
                // min, max, and start define the range of pieces a player can place to start the game
                new GameRules.Arsenal(0, 1, 1, p1, pieceTypeFlag),
                new GameRules.Arsenal(0, 6, 6, p1, pieceTypeBomb),
                new GameRules.Arsenal(0, 1, 1, p1, pieceTypeSpy),
                new GameRules.Arsenal(0, 8, 8, p1, pieceTypeScout),
                new GameRules.Arsenal(0, 5, 5, p1, pieceTypeMiner),
                new GameRules.Arsenal(0, 4, 4, p1, pieceType4),
                new GameRules.Arsenal(0, 4, 4, p1, pieceType4),
                new GameRules.Arsenal(0, 4, 4, p1, pieceType6),
                new GameRules.Arsenal(0, 3, 3, p1, pieceType7),
                new GameRules.Arsenal(0, 2, 2, p1, pieceType8),
                new GameRules.Arsenal(0, 1, 1, p1, pieceType9),
                new GameRules.Arsenal(0, 1, 1, p1, pieceTypeM),

                new GameRules.Arsenal(0, 1, 1, p2, pieceTypeFlag),
                new GameRules.Arsenal(0, 6, 6, p2, pieceTypeBomb),
                new GameRules.Arsenal(0, 1, 1, p2, pieceTypeSpy),
                new GameRules.Arsenal(0, 8, 8, p2, pieceTypeScout),
                new GameRules.Arsenal(0, 5, 5, p2, pieceTypeMiner),
                new GameRules.Arsenal(0, 4, 4, p2, pieceType4),
                new GameRules.Arsenal(0, 4, 4, p2, pieceType4),
                new GameRules.Arsenal(0, 4, 4, p2, pieceType6),
                new GameRules.Arsenal(0, 3, 3, p2, pieceType7),
                new GameRules.Arsenal(0, 2, 2, p2, pieceType8),
                new GameRules.Arsenal(0, 1, 1, p2, pieceType9),
                new GameRules.Arsenal(0, 1, 1, p2, pieceTypeM),
            };

            var boardlayout = new[, ]
            {
                // x, y     so first row is actually the first column
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, _____Space, _____Space, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, _____Space, _____Space, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, waterSpace, waterSpace, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, waterSpace, waterSpace, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, _____Space, _____Space, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, _____Space, _____Space, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, waterSpace, waterSpace, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, waterSpace, waterSpace, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, _____Space, _____Space, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
                { plyr1Space, plyr1Space, plyr1Space, plyr1Space, _____Space, _____Space, plyr2Space, plyr2Space, plyr2Space, plyr2Space },
            };

            // populate the piece colleciton with the rule's number of pieces
            foreach (GameRules.Arsenal ars in Arsenal)
            {
                for (int i = 0; i < ars.CountStart; i++)
                {
                    pieces.Add(new Piece(-1, -1, ars.Owner, ars.Type)
                    {
                        pos = Piece.removedPos // ensure unplaced pieces are flagged correctly
                    });
                }
            }

            Random rand = new Random();

            int countPlaced = 0;
            // place the pieces on the board
            for (int x = 0; x < 10; x++)
            {
                for (int y = 0; y < 10; y++)
                {
                    var unplacedPieces = pieces.Where(q => q.Owner == boardlayout[x, y].StarterPlace && q.pos.X < 0).ToList();

                    // if the selected board location does not belong to a player
                    if (unplacedPieces.Count == 0)
                    {
                        continue;
                    }

                    // get a random piece from the collection that has not been placed yet
                    Piece randomPiece = unplacedPieces.ElementAt(rand.Next(0, unplacedPieces.Count));
                    randomPiece.pos = new CoordAbs(x, y);
                    countPlaced++;
                }
            }

            // no piece should be remaining off the board
            System.Diagnostics.Debug.Assert(!pieces.Any(q => q.pos.X < 0));

            #endregion

            GameRules rules = new GameRules(

                _arsenal: Arsenal,

                _players: new List <Player>()
            {
                p1,
                p2
            },

                _startingBoard: new Board()
            {
                Height          = 10,
                Width           = 10,
                LocationsLayout = boardlayout,
                PiecesLayout    = new Piece[10, 10],
                PieceSet        = pieces,
            }
                )
            {
                // Function to determine if a player automatically wins (true), lost (false), or is still active in game (null)
                TerminalStateFunction = (theRules, board, player) =>
                {
                    bool?  result = null;
                    string reason = "unknown";

                    // over turn limit is a loss - used for turn planning
                    if (board.TurnNumber >= theRules.MaxPhysicalTurns)
                    {
                        reason = "Exceeded maximum turns";
                        result = false;
                    }

                    // game is over if player lost their flag
                    if (!board.PieceSet.Exists(x => x.Type == pieceTypeFlag &&
                                               x.pos != Piece.removedPos &&
                                               x.Owner == player))
                    {
                        reason = "Lost flag";
                        result = false;
                    }

                    // if we lost all our movable pieces
                    bool lostAllPieces = board.PieceSet.Where(x => x.Owner == player &&
                                                              x.Type.Movable)
                                         .All(x => x.pos == Piece.removedPos);
                    if (lostAllPieces)
                    {
                        reason = "Lost all mobile pieces";
                        result = false;
                    }

                    if (theRules.LoggingSettings.winLossReasons)
                    {
                        if (result == true)
                        {
                            Console.WriteLine(player + " won ... " + reason);
                        }
                        if (result == false)
                        {
                            Console.WriteLine(player + " lost ... " + reason);
                        }
                    }

                    return(result); // otherwise still in game
                },
            };


            Game game = new Game()
            {
                GameNumber   = 1,
                rules        = rules,
                CurrentBoard = new Board(rules.InitialBoard), // need to ensure it makes a deep copy
            };

            return(game);
        }
Example #9
0
        /// <summary>
        /// Given a pice and its new location, check if the move is allowed based on obstructions, limits, etc
        /// </summary>
        /// <param name="p"></param>
        /// <param name="targetCoord"></param>
        /// <param name="currentBoard"></param>
        private bool InitializeAndValidateMove(Piece p, CoordAbs targetCoord, Board currentBoard, GameRules rules)
        {
            FromCoord     = new CoordAbs(p.pos.X, p.pos.Y);
            movingPiece   = p;
            outcome       = GameRules.MoveOutcomes.Unknown;
            opponentPiece = null;
            ToCoord       = targetCoord;

            // finish populating the Move and check the legality

            Legal = false;

            // ensure the move stays on the board
            if (ToCoord.X >= currentBoard.Width ||
                ToCoord.X < 0 ||
                ToCoord.Y >= currentBoard.Height ||
                ToCoord.Y < 0)
            {
                return(false);
            }

            opponentPiece = currentBoard.GetPieceAtCoord(ToCoord); // may be null

            if (opponentPiece?.Owner == movingPiece.Owner || // cannot move into owned space
                currentBoard.GetLocationAtCoord(ToCoord)?.Passable == false)    // cannot move into obstacles
            {
                return(false);
            }

            // check if theres is an empty line to the target location
            if (p.Type.CanJump == false)
            {
                if (rules.LoggingSettings.debugJumpchecks)
                {
                    Console.WriteLine($"Checking space between {FromCoord} to {ToCoord}...");
                }

                int xmin = Math.Min(FromCoord.X, ToCoord.X);
                int xmax = Math.Max(FromCoord.X, ToCoord.X);
                int ymin = Math.Min(FromCoord.Y, ToCoord.Y);
                int ymax = Math.Max(FromCoord.Y, ToCoord.Y);

                for (int y = ymin; y <= ymax; y++)
                {
                    for (int x = xmin; x <= xmax; x++)
                    {
                        // dont include the coords of FromCoord nor ToCoord, just between
                        if (FromCoord.X == x && FromCoord.Y == y ||
                            ToCoord.X == x && ToCoord.Y == y)
                        {
                            continue;
                        }

                        // check if the path is clear of empty pieces and is traversable
                        if (currentBoard.PiecesLayout[x, y] != null || !currentBoard.LocationsLayout[x, y].Passable)
                        {
                            Legal = false;
                            return(false);
                        }

                        if (rules.LoggingSettings.debugJumpchecks)
                        {
                            Console.WriteLine($"{x} {y} is " + currentBoard.PiecesLayout[x, y]
                                              + " / " + currentBoard.LocationsLayout[x, y].Passable);
                        }
                    }
                }
            }


            // todo: is legality and battle result predictions premature at this stage?
            // outcome = movingPiece.PredictWinner(opponentPiece, rules);//defaults to "move" if nothing is in the way

            Legal = true;
            return(true);
        }
Example #10
0
 public Move(Piece p, CoordAbs targetCoord, Board currentBoard, GameRules rules)
 {
     InitializeAndValidateMove(p, targetCoord, currentBoard, rules);
 }
Example #11
0
 public Move(Piece p, CoordRel targetCoord, Board currentBoard, GameRules rules)
 {
     InitializeAndValidateMove(p, targetCoord.ToAbs(p.pos.X, p.pos.Y), currentBoard, rules);
 }