Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

C# Project I'm struggling with getting this new class to work. I need to build a class called GroupNPlayer that implements the IPlayer interface and

C# Project

I'm struggling with getting this new class to work. I need to build a class called GroupNPlayer that implements the IPlayer interface and takes user input for the 'GetAttackPosition' method so that the user can input grid coordinates such as A5 compare for a hit and then the AI will cycle through their turns and so forth, until game is complete. Below is the Game code which must be used, I just need to figure out how to build an actual user controlled, player object that will take in grid coordinates and function with the rest of the game:

internal class MultiPlayerBattleShip { const int GridSize = 10; //Your player should work when GridSize >=7

private static readonly Random Random = new Random();

private readonly List _players;

private List _playerGrids; private List _playerShips; private List currentPlayers;

public MultiPlayerBattleShip(List players) { this._players = players; }

internal void Play(PlayMode playMode) { currentPlayers = new List(); var availablePlayers = new List(_players); _playerGrids = new List(); _playerShips = new List();

//Add each player in a random order for (int i = 0; i < _players.Count; i++) { var player = availablePlayers[Random.Next(availablePlayers.Count)]; availablePlayers.Remove(player); currentPlayers.Add(player); }

//Tell each player the game is about to start for (int i=0; i { var ships = new Ships(); ships.Add(new AircraftCarrier()); ships.Add(new Submarine()); ships.Add(new Destroyer()); ships.Add(new Destroyer()); ships.Add(new PatrolBoat()); ships.Add(new PatrolBoat()); ships.Add(new PatrolBoat()); ships.Add(new Battleship());

var count = ships._ships.Count(); int totalLength = ships._ships.Sum(ship => ship.Length); currentPlayers[i].StartNewGame(i, GridSize, ships);

//Make sure player didn't change ships if (count != ships._ships.Count() || totalLength != ships._ships.Sum(ship => ship.Length)) { throw new Exception("Ship collection has ships added or removed"); }

var grid = new Grid(GridSize); grid.Add(ships); _playerGrids.Add(grid); _playerShips.Add(ships); }

int currentPlayerIndex = 0; while (currentPlayers.Count > 1) { var currentPlayer = currentPlayers[currentPlayerIndex];

//Ask the current player for their move Position pos = currentPlayer.GetAttackPosition();

//Work out if anything was hit var results = CheckAttack(pos);

//Notify each player of the results foreach (var player in currentPlayers) { player.SetAttackResults(results); }

DrawGrids();

Console.WriteLine(" Player " + currentPlayer.Index + "[" + currentPlayer.Name + "] turn."); Console.WriteLine(" Attack: " + pos.X + "," + pos.Y); Console.WriteLine(" Results:"); foreach (var result in results) { Console.Write(" Player " + result.PlayerIndex + " " + result.ResultType); if (result.ResultType == AttackResultType.Sank) { Console.Write(" - " + result.SunkShip); } Console.WriteLine(); }

//Remove any ships with sunken battleships //Iterate backwards so that we don't mess with the indexes for (int i = currentPlayers.Count - 1; i >= 0; --i) { var player = currentPlayers[i]; if (_playerShips[player.Index].SunkMyBattleShip) { currentPlayers.Remove(player); //We never want to remvoe all the players... if (currentPlayers.Count == 1) { break; } } }

//Move to next player wrapping around the end of the collection currentPlayerIndex = (currentPlayerIndex + 1)%currentPlayers.Count;

if (playMode == PlayMode.Pause) { Console.WriteLine(" Press a key to continue"); Console.ReadKey(true); } else { Thread.Sleep(2000); } }

Console.WriteLine(); Console.WriteLine("Winner is '" + currentPlayers[0].Name + "'"); Console.ReadKey(true);

}

private List CheckAttack(Position pos) { var results = new List();

foreach (var player in currentPlayers) { var result = _playerShips[player.Index].Attack(pos);

//Mark attacks on the grid foreach (var grid in _playerGrids) { grid.Attack(pos); }

result.PlayerIndex = player.Index; results.Add(result); } return results; }

private void DrawGrids() { Console.Clear(); int drawX = 0; int drawY = 0;

for (int i=0; i < currentPlayers.Count; i++) { var player = currentPlayers[i]; var playerIndex = player.Index;

var grid = _playerGrids[playerIndex]; Console.SetCursorPosition(drawX, drawY); Console.ForegroundColor = ConsoleColor.Black; Console.BackgroundColor = ConsoleColor.White;

Console.Write(player.Name); grid.Draw(drawX, drawY+1);

drawX += GridSize + 4; if (drawX + GridSize > Console.BufferWidth) { drawY += GridSize + 5; drawX = 0; } } }

}

public class Grid { private readonly GridEntry[,] _grid; private readonly int _gridSize;

public Grid(int gridSize) { _gridSize = gridSize; _grid = new GridEntry[gridSize,gridSize]; //Fill the grid with empty entries marked as not hit for (int x = 0; x < gridSize; x++) { for (int y = 0; y < gridSize; y++) { _grid[x,y] = new GridEntry(); } } }

public void Add(Ships ships) { foreach (var ship in ships._ships) { if (ship.Positions == null) { throw new ArgumentException("A player has not set the ships positions"); }

foreach (var pos in ship.Positions) { if (pos.X< 0 || pos.X >_gridSize || pos.Y <0 || pos.y>= _gridSize) { throw new ArgumentException("One of the ships is outside the grid"); }

if (pos.Hit) { throw new ArgumentException("One of the players is adding a hit ship to the game"); }

if (_grid[pos.X, pos.Y].Ship != null) { throw new ArgumentException("One of the players has an overlapping ship"); }

_grid[pos.X, pos.Y].Ship = ship; } } }

public void Draw(int drawX, int drawY) { for (int x = 0; x < _gridSize; x++) { for (int y = 0; y < _gridSize; y++) { Console.SetCursorPosition(drawX + x, drawY + y); Console.ForegroundColor = (_grid[x, y].Ship == null) ? ConsoleColor.Gray : _grid[x, y].Ship.Color; //Find if this segment of the ship is hit Console.BackgroundColor = (_grid[x,y].Hit)? ConsoleColor.Red : ConsoleColor.Black; if (_grid[x, y].Ship == null) { Console.Write("."); } else { Console.Write(_grid[x, y].Ship.Character); } } }

//Reset colors Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.White; }

public void Attack(Position pos) { _grid[pos.X, pos.Y].Hit = true; } }

public class GridEntry { public bool Hit; public Ship Ship; }

interface IPlayer { ///

/// Initializes the players are the start of a game and returns the positions of the ships /// Note: This method should be used to reset any AI state. It will be called once per game and each session might be multiple games /// You may also use this to generate new data structures for this game. The Test harness will handle checking for hits based on your /// returned value so it is up to you if and how you want to store the representation of your own grid ///

///What is the index of this player for this game - may change each game ///Size of the square grid - may change each game ///A list of Ships to provide positions for - may change each game. You should populate this collection with positions void StartNewGame(int playerIndex, int gridSize, Ships ships);

///

/// The name of this player - displayed in the UI ///

String Name { get; }

///

/// The index of this player - it should return the index passed into the StartNewGame ///

int Index { get; }

///

/// This is where you put the AI that chooses which square to target ///

/// A position with an x, y coordinate Position GetAttackPosition();

///

/// The game will notify you of the results of each attack. ///

///A collection for each player still in the game /// You will get the index, the attack position and the result of the attack void SetAttackResults(List results); }

public enum PlayMode { Delay, Pause, }

internal class DumbPlayer : IPlayer { private static int _nextGuess = 0; //Note static - we all share the same guess

private int _index; private int _gridSize;

public DumbPlayer(string name) { Name = name; } public void StartNewGame(int playerIndex, int gridSize, Ships ships) { _gridSize = gridSize; _index = playerIndex;

//DumbPlayer just puts the ships in the grid one on each row int y = 0; foreach (var ship in ships._ships) { ship.Place(new Position(0, y++), Direction.Horizontal); } }

public Position GetAttackPosition() { //A *very* naive guessing algorithm that simply starts at 0, 0 and guess each square in order //All 'DumbPlayers' share the counter so they won't guess the same one //But we don't check to make sure the square has not been guessed before var pos = new Position(_nextGuess % _gridSize, (_nextGuess /_gridSize)); _nextGuess++; return pos; }

public void SetAttackResults(List results) { //DumbPlayer does nothing with these results - its going to keep making dumb guesses }

public string Name { get; }

public int Index => _index; }

internal class RandomPlayer : IPlayer { private static readonly List Guesses = new List(); private int _index; private static readonly Random Random = new Random(); private int _gridSize;

public RandomPlayer(string name) { Name = name; }

public void StartNewGame(int playerIndex, int gridSize, Ships ships) { _gridSize = gridSize; _index = playerIndex;

GenerateGuesses();

//Random player just puts the ships in the grid in Random columns //Note it cannot deal with the case where there's not enough columns //for 1 per ship var availableColumns = new List(); for (int i = 0; i < gridSize; i++) { availableColumns.Add(i); }

foreach (var ship in ships._ships) { //Choose an X from the set of remaining columns var x = availableColumns[Random.Next(availableColumns.Count)]; availableColumns.Remove(x); //Make sure we can't pick it again

//Choose a Y based o nthe ship length and grid size so it always fits var y = Random.Next(gridSize - ship.Length); ship.Place(new Position(x, y), Direction.Vertical); } }

private void GenerateGuesses() { //We want all instances of RandomPlayer to share the same pool of guesses //So they don't repeat each other.

//We need to populate the guesses list, but not for every instance - so we only do it if the set is missing some guesses if (Guesses.Count < _gridSize*_gridSize) { Guesses.Clear(); for (int x = 0; x < _gridSize; x++) { for (int y = 0; y < _gridSize; y++) { Guesses.Add(new Position(x,y)); } } } }

public string Name { get; } public int Index => _index;

public Position GetAttackPosition() { //RandomPlayer just guesses random squares. Its smart in that it never repeats a move from any other random //player since they share the same set of guesses //But it doesn't take into account any other players guesses var guess = Guesses[Random.Next(Guesses.Count)]; Guesses.Remove(guess); //Don't use this one again return guess; }

public void SetAttackResults(List results) { //Random player does nothing useful with these results, just keeps on making random guesses } }

public abstract class Ship {

private Position[] _positions; public readonly int Length; public readonly ConsoleColor Color; public readonly ShipTypes ShipType; private static readonly char[] Characters = {'?', 'P', 'S', 'D', 'A', 'B'};

public virtual bool IsBattleShip => false;

protected Ship(int length, ConsoleColor color, ShipTypes shipType) { Length = length; Color = color; ShipType = shipType; }

public void Reset() { _positions = null; }

public char Character => Characters[(int) ShipType];

public Position[] Positions { get { if (_positions == null) { return null; } //Return a copy since this is a readonly field var retval = new Position[_positions.Length]; Array.Copy(_positions, retval, _positions.Length); return retval; } }

public void Place(Position start, Direction direction) { _positions = new Position[Length]; for (int i = 0; i < Length; i++) { _positions[i] = new Position(start.X, start.Y); if (direction == Direction.Horizontal) start.X++; if (direction == Direction.Vertical) start.Y++; } }

public AttackResult Attack(Position pos) { foreach (var position in _positions) { if (position.X == pos.X && position.Y == pos.Y) { position.Hit = true; if (Sunk) { return new AttackResult(0, pos, AttackResultType.Sank, ShipType); } return new AttackResult(0, pos, AttackResultType.Hit); } }

return new AttackResult(0, pos); //Miss }

public bool Sunk { get { return _positions.All(position => position.Hit); } } }

public class Ships { public readonly List _ships = new List();

public void Clear() { _ships.Clear(); }

public bool SunkMyBattleShip { get { //Find the battleship and see if its sunk foreach (var ship in _ships) { var battleShip = ship as Battleship; if (battleShip != null) { return battleShip.Sunk; }

}

throw new Exception("Cannot find a battleship"); } }

public void Add(Ship ship) { _ships.Add(ship); }

public AttackResult Attack(Position pos) { //Search the positions for a hit foreach (var ship in _ships) { AttackResult attackResult = ship.Attack(pos); if (attackResult.ResultType != AttackResultType.Miss) { return attackResult; //Once we hit then no point looking any more } }

//No hits means a miss! return new AttackResult(0, pos); }

}

public class Position

{ public int X; public int Y; public bool Hit; public Position(int x, int y) { X = x; Y = y; Hit = false; } }

public enum Direction { Horizontal, Vertical, }

public enum ShipTypes { None, PatrolBoat, Submarine, Destroyer, AircraftCarrier, Battleship, }

class AircraftCarrier : Ship { public AircraftCarrier() : base(5, ConsoleColor.Blue, ShipTypes.AircraftCarrier) {

}

}

class Battleship : Ship { public Battleship() : base(4, ConsoleColor.DarkGreen, ShipTypes.Battleship) {

}

public override bool IsBattleShip => true; }

class Destroyer : Ship { public Destroyer() : base(3, ConsoleColor.DarkRed, ShipTypes.Destroyer) {

}

}

class PatrolBoat : Ship { public PatrolBoat() : base(2, ConsoleColor.White, ShipTypes.PatrolBoat) {

}

}

class Submarine : Ship { public Submarine() : base(3, ConsoleColor.Cyan, ShipTypes.Submarine) {

}

}

public enum AttackResultType { Miss, Hit, Sank, }

public struct AttackResult { public int PlayerIndex; public Position Position; public AttackResultType ResultType; public ShipTypes SunkShip; //Filled in if ResultType is Sunk

public AttackResult(int playerIndex, Position position, AttackResultType attackResultType= AttackResultType.Miss, ShipTypes sunkShip = ShipTypes.None) { PlayerIndex = playerIndex; Position = position; ResultType = attackResultType; SunkShip = sunkShip; } }

class Program { static void Main(string[] args) { List players = new List(); players.Add(new DumbPlayer("Dumb 1")); players.Add(new RandomPlayer("Random 1"));

//Your code here //players.Add(new GroupNPlayer());

MultiPlayerBattleShip game = new MultiPlayerBattleShip(players); game.Play(PlayMode.Pause); } }

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

More Books

Students also viewed these Databases questions

Question

What is a processing time?

Answered: 1 week ago

Question

2. What efforts are countries making to reverse the brain drain?

Answered: 1 week ago