Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

C# Question: Battleship AI~ The AI should have three modes, 1) Hunt 2) Agression

ID: 3805406 • Letter: C

Question

C# Question:

Battleship AI~

The AI should have three modes, 1) Hunt 2) Agression 3)Sink

Hunt should be a method to select cells within the array to attck obviously

Agression should be how upon detection of an enemy ship the AI should focus on attacking allr emaining cells systematically until that ship has been sunk

Sink should be the after the AI sinks a ship, it declairs what ship was sunk and return to hunt mode for the following turn.

Implement a multiplayer battleship AI.

The rules are the same as last week.

The game is played on an NxN grid

Each player will place a specified set of ships

The ships will vary in length from 2 to 5.

There can be any number or any size ship including 0

EXCEPT the battleship – which there will always be 1 and only 1

Player order will be random but fixed at the start of the game

Each round consists of each player making a grid selection in turn

Each grid selection will be played into all player’s grid. Including the current players grid

Each player will respond with HIT, MISS or SANK {ship name}

If a player’s battleship is sunk that player is removed from the game

Repeat from #4 until 1 player remains

That player is the winner

Design update

Examine the test framework from blackboard.

Review the IPlayer interface – this is the entry point to your code. You will need to implement each of these functions. Does this interface provide you with enough information to implement? If not speak to your instructor – maybe accommodations can be made

Review the DumbPlayer and RandomPlayer implementations to see an example of how simple AIs can be built.

Run the code and watch the game play out between the example players

Complete the AI

Your code should be able to play against the provided AIs and also will be challenged by the AIs from other teams as well as one from the instructor

If your code takes an unreasonable amount of time or crashes then your player will be removed from the game.

Since you have the test harness you should be able to run many test passes yourself against the sample AIs

Note any attempts to use reflection or any other similar techniques to determine other players positions will be considered cheating and result in 0 for the assignment.

Grid.cs --

using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;

namespace Week6
{
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;
}
}
}

GridEntry.cs --

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

IPlayer.cs --

using System;
using System.Collections.Generic;

namespace Week6
{
interface IPlayer
{
/// <summary>
/// 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
/// </summary>
/// <param name="playerIndex">What is the index of this player for this game - may change each game</param>
/// <param name="gridSize">Size of the square grid - may change each game</param>
/// <param name="ships">A list of Ships to provide positions for - may change each game. You should populate this collection with positions</param>
void StartNewGame(int playerIndex, int gridSize, Ships ships);

/// <summary>
/// The name of this player - displayed in the UI
/// </summary>
String Name { get; }

/// <summary>
/// The index of this player - it should return the index passed into the StartNewGame
/// </summary>
int Index { get; }

/// <summary>
/// This is where you put the AI that chooses which square to target
/// </summary>
/// <returns>A position with an x, y coordinate</returns>
Position GetAttackPosition();

/// <summary>
/// The game will notify you of the results of each attack.
/// </summary>
/// <param name="results">A collection for each player still in the game
/// You will get the index, the attack position and the result of the attack</param>
void SetAttackResults(List<AttackResult> results);
}
}

MultiPlayerBattleShip.cs --

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;

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

private static readonly Random Random = new Random();

private readonly List<IPlayer> _players;

private List<Grid> _playerGrids;
private List<Ships> _playerShips;

private List<IPlayer> currentPlayers;


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

internal void Play(PlayMode playMode)
{
currentPlayers = new List<IPlayer>();
var availablePlayers = new List<IPlayer>(_players);
_playerGrids = new List<Grid>();
_playerShips = new List<Ships>();

//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<currentPlayers.Count; 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<AttackResult> CheckAttack(Position pos)
{
var results = new List<AttackResult>();

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;
}
}
}

}
}

PlayMode.cs --

namespace Week6
{
public enum PlayMode
{
Delay,
Pause,
}
}

Program.cs --

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Week6
{
class Program
{
static void Main(string[] args)
{


List<IPlayer> players = new List<IPlayer>();
players.Add(new DumbPlayer("Dumb 1"));
players.Add(new DumbPlayer("Dumb 2"));
players.Add(new DumbPlayer("Dumb 3"));
players.Add(new RandomPlayer("Random 1"));
players.Add(new RandomPlayer("Random 2"));
players.Add(new RandomPlayer("Random 3"));
players.Add(new RandomPlayer("Random 4"));
players.Add(new RandomPlayer("Random 5"));

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

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

Explanation / Answer

int shipCounter = 0, trend = 0; static Random rnd = new Random(); bool gameOver = false, playerTurn = false; int[] score = { 0, 0 }; struct gameData { public bool occupied, hit, marked; } gameData[,,] data; public void computerMove() { Point target = seekTarget(); try { if (data[1, target.X, target.Y].hit) computerMove(); else { data[1, target.X, target.Y].hit = true; if (data[1, target.X, target.Y].occupied) { attacking = true; score[0]++; computerMove(); } } playerTurn = true; } catch (IndexOutOfRangeException) { computerMove(); } } public Point seekTarget() { Point origin = new Point(-1, -1); //find a point that's been hit. int x = 0, y = 0; while (x = -1 && ((!data[1, lim[0].X, lim[0].Y].hit && !data[1, lim[0].X, lim[0].Y].occupied) || (data[1, lim[0].X, lim[0].Y].hit && data[1, lim[0].X, lim[0].Y].occupied))) { lim[1].Y--; if (lim[1].Y == -1) break; } while (lim[2].X