|
Repo: https://github.com/Charles-CarM/Battleship
I am currently building my battleship console application in C#. I have prior experience
building projects with Vanilla JS and in React. I went through a tutorial on C# and could
grasp most of the concepts pretty well. I have jumped into project building because I know
this is where the maximum amount of growth will be as a developer. With that being said I
will lay out the issues arising in my application.
requirements
* the ship has a length of 5
* ship will be randomly assigned on a 10X10 grid
* when the ship is hit five times it is sunk and game is over
I am having trouble choosing the best way to implement the 10x10 gameboard grid. With
some searching on the web I have came across the DataTable class, which allows for rows
and columns to be set in a table(I believe this to be the better approach), and I have attempted
to get them displayed in the Console below. Also I know that I can create a 10x10 grid with
a 2D array using int[,] grid = [10,10]; however I have realized that I can't assign the random values for the ship with an already populated array. I would like to get some feedback on how
I can move forward with creation of the gameboard, and random assignment of the ship on the board.
I know this post and my code are a little messy so please excuse me on that part. Any solutions or suggestions you recommend for me moving forward are welcomed and greatly appreciated. Hope you are very well wherever you may be.
using System.Data;
namespace Battleship
{
class Program
{
static void Main(string[] args)
{
GamePlay ship = new GamePlay("ten", "fast ship", 350);
Console.ReadLine();
GreetUser();
DataTable gameBoard = new DataTable("Battleship");
DataColumn columnA = new DataColumn("A");
DataColumn columnB = new DataColumn("B");
DataColumn columnC = new DataColumn("C");
DataColumn columnD = new DataColumn("D");
DataColumn columnE = new DataColumn("E");
DataColumn columnF = new DataColumn("F");
DataColumn columnG = new DataColumn("G");
DataColumn columnH = new DataColumn("H");
DataColumn columnI = new DataColumn("I");
DataColumn columnJ = new DataColumn("J");
gameBoard.Columns.Add(columnA);
gameBoard.Columns.Add(columnB);
gameBoard.Columns.Add(columnC);
gameBoard.Columns.Add(columnD);
gameBoard.Columns.Add(columnE);
gameBoard.Columns.Add(columnF);
gameBoard.Columns.Add(columnG);
gameBoard.Columns.Add(columnH);
gameBoard.Columns.Add(columnI);
gameBoard.Columns.Add(columnJ);
DataRow row1 = gameBoard.NewRow();
DataRow row2 = gameBoard.NewRow();
DataRow row3 = gameBoard.NewRow();
DataRow row4 = gameBoard.NewRow();
DataRow row5 = gameBoard.NewRow();
DataRow row6 = gameBoard.NewRow();
DataRow row7 = gameBoard.NewRow();
DataRow row8 = gameBoard.NewRow();
DataRow row9 = gameBoard.NewRow();
DataRow row10 = gameBoard.NewRow();
row1["A"] = 0;
row1["B"] = 0;
row1["C"] = 0;
row1["D"] = 0;
row1["E"] = 0;
row1["F"] = 0;
row1["G"] = 0;
row1["H"] = 0;
row1["I"] = 0;
row1["J"] = 0;
gameBoard.Rows.Add(row1);
gameBoard.Rows.Add(row2);
gameBoard.Rows.Add(row3);
gameBoard.Rows.Add(row4);
gameBoard.Rows.Add(row5);
gameBoard.Rows.Add(row6);
gameBoard.Rows.Add(row7);
gameBoard.Rows.Add(row8);
gameBoard.Rows.Add(row9);
gameBoard.Rows.Add(row10);
for(int j = 0; j < gameBoard.Rows.Count; j++)
{
for (int i = 0; i < gameBoard.Columns.Count; i++)
{
Console.WriteLine(gameBoard.Columns[i].ColumnName + " ");
Console.WriteLine(gameBoard.Rows[j].ItemArray[i]);
}
}
Console.ReadLine();
string coords;
int[,] numberGrid =
{
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 },
{ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 },
{ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 },
{ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 },
{ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 },
{ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70 },
{ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 },
{ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90 },
{ 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 }
};
int[,] freshGrid = new int[10, 10];
for(int j = 0; j < freshGrid.Length; j++)
{
coords = Convert.ToString(j);
Console.Write(coords);
}
Console.ReadLine();
Console.WriteLine(freshGrid[3,4]);
Console.ReadLine();
Random random = new Random();
int newShip = random.Next();
Console.ReadLine();
Console.WriteLine("\nEnter guess: ");
int guess = Convert.ToInt32(Console.ReadLine());
Console.WriteLine($"\nYou guessed: {guess}");
Console.ReadLine();
}
static void GreetUser()
{
Console.WriteLine("Welcome to Battle! press Enter ");
Console.ReadLine();
Console.WriteLine("Enter username: ");
string username = Console.ReadLine();
Console.WriteLine($"\nLet's begin {username}!");
}
}
}
|
|
|
|
|
Why are you using a DataTable? It's a bit of a sledgehammer to crack a nut ...
You need a 10 x 10 area of "squares" which can contain four values: Empty, Ship, Hit, and Miss. So the simplest way to do that is you create an enum that has all of those values:
public enum Square
{
Empty,
Ship,
Hit,
Miss,
}
You can then create an array of that:
Square[,] MyBoard = new Square[10,10]; Or two:
Square[,] HisBoard = new Square[10,10]; You can now reference any particular Square via indexes:
if (MyBoard[x, y] == Square.Ship)
{
MyBoard[x, y] = Square.Hit;
...
} Create a method to clear a board:
public static Square[,] Clear(Square[,] board)
{
for (int x = 0; x < board.GetLength(0); x++)
{
for (int y = 0; y < board.GetLength(1); y++)
{
board[x, y] = Square.Empty;
}
}
return board;
} And you can start getting ready for a new game:
Square[,] MyBoard = new Square[10,10];
Square[,] HisBoard = new Square[10,10];
Clear(MyBoard);
Clear(HisBoard); Then just write another method to load your ships in: it accepts a board as a parameter, calls Clear. and then decides where to put the ships. (Initially, I'd try to write it using "single space" ships to make the code easier, then start working on the more complex shapes you really need).
In fact what I'd do is more complex than that: I'd encapsulate the Square array in a class called Board, several ship classes (Carrier, BattleShip, Cruiser, Submarine, Destroyer) derived from a base Ship class) and let it handle the mundane stuff so that most of the time you are manipulating Board objects:
MyBoard.place(myDestroyer, x, y, Orientation.Horizontal);
MyBoard.Bomb(6, 7); But if you haven't reached classes and instances yet then a basic array will be fine.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Thanks Griff for taking the time to read my post and provide meaningful feedback.
Haha I will opt for the nutcracker instead of the sledgehammer moving forward.
I was experimenting with the DataTable class due to the appeal of Rows/Columns in
a table, but that's not the best approach as you've laid out. I follow what you are
saying with the 10x10 area of squares that have the four possible enum values.
Then calling the Square to create the array for the game board. Using the if/else logic to Referencing the specific squares. Next creating the nested x,y for loops to clear the board, and then being able to start the new game by calling an instance of the Square[10,10] grid for each
player.
I follow you on creating a method to load the ships into, and at first trying single space ships(get that working ) before moving onto more complex logic with the ship lengths.
I'll have to do a deeper dive on encapsulating the Square array in a Board class, then a base Ship
class within to create the ships, but this seems like the best approach to implement.
Yeah I haven't had much practice with classes and instances up to this point, but have a surface
level understanding of them.
I am mostly just repeating back to you what you've told me in this post, but it's extremely
helpful for my understanding and guiding me along the way.
Thanks again Griff!
|
|
|
|
|
You're welcome!
If you get stuck, just ask again - we're all pretty willing to help.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I am a little bit further along in my Battleship app although
not as far as I'd hoped to be.
So I setup the 10x10 representations with the enum
Square class and values on the board
It feels like I am running into issues with the CheckGuess
Method in the location where I am calling it from, and also
with the values it's returning after the player enters their guess.
I know that I can change TheBoard values because I have done
so in the current code and wrote it out at the bottom with the
changed values displayed. I can't quite understand why those
same values don't show up when the players guess gets passed
into the CheckGuess Method.
{explained as best as possible to my knowledge}
Please help if you have free time
GitHub - Charles-CarM/Battleship[^]
namespace ConsoleBattle
{
public class Program
{
public enum Square
{
Water,
Miss,
Ship,
Hit,
}
public static void Main(string[] args)
{
bool finished = false;
string message;
Square[,] TheBoard = new Square[10, 10];
Welcome();
Console.WriteLine("X: 0 - 9");
Console.WriteLine("Y: 0 - 9");
Console.WriteLine("Enter your guess: X, Y");
while (!finished)
{
TheBoard[7, 7] = Square.Ship;
TheBoard[7, 5] = Square.Ship;
string guess = Console.ReadLine();
void CheckGuess(int x, int y)
{
if (TheBoard[x, y] == Square.Ship)
{
Console.WriteLine(TheBoard[x, y] = Square.Hit);
}
else if (TheBoard[x, y] == Square.Water)
{
Console.WriteLine(TheBoard[x, y] = Square.Miss);
}
else if (TheBoard[x, y] == Square.Miss)
{
Console.WriteLine(message = "you already missed here");
}
else
{
Console.WriteLine(message = "this spot was hit already");
}
Console.ReadLine();
}
try
{
int xPos = Convert.ToInt32(guess.Split(',')[0]) - 1;
int yPos = Convert.ToInt32(guess.Split(',')[1]) - 1;
if (xPos > 9 || yPos > 9)
{
message = "You are off the board, try again!";
}
CheckGuess(xPos, yPos);
}
catch
{
message = "Unable to process coordinates";
}
finished = true;
}
Console.WriteLine(TheBoard[7, 7]);
Console.WriteLine(TheBoard[7, 5]);
}
private static void Welcome()
{
Console.WriteLine("Welcome to Console Battle!");
Console.WriteLine("Enter username: ");
string username = Console.ReadLine();
Console.WriteLine($"\nLet's begin {username} press Enter!");
Console.ReadLine();
}
}
}
|
|
|
|
|
OK ... first off, to play battleships you need at least two boards: one with your ships on, one with his.
So write your methods with that in mind! If you don't, when you come to add the second board, you have to mess around with the existing, tested, working code to get it to work with extra boards. And remember, on the "hardware" version of the game, you have a total of four boards: Your ships / his hits, your hits his ships; and the same pair for him.
Guilty Gadgets Battleships Sea Battle Traditional Family Fun Combat Strategy Board Game : Amazon.co.uk: Toys & Games[^]
While you can do it with just the one, it's a clumsy solution that only saves about 10 * 10 * 4 * 3 bytes and makes your code a lot clumsier.
And don't declare methods inside other methods: it makes the code clumsy and hard to read.
I'd start with a class: give it a board area, and add the CheckGuess method to that. Then create four instances of the class in your main method.
public class BattleshipsBoard
{
private Square[,] TheBoard = new Square[10, 10];
public Square CheckGuess(int x, int y)
{
...
return whatTheSquareContained;
}
}
...
public static void Main(string[] args)
{
BattleshipsBoard myShips = new BattleshipsBoard();
BattleshipsBoard hisShips = new BattleshipsBoard();
BattleshipsBoard myGuesses = new BattleshipsBoard();
BattleshipsBoard hisGuesses = new BattleshipsBoard();
... Now all the boards are independant and you can query any of them the same way:
if (myShips.CheckGuess(hisX, hisY) == Square.Ship)
{
... Or
if (hisShips.CheckGuess(myX, myY) == Square.Ship)
{
... Your code becomes more readable, and easier to work with.
Give it a try!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Hello there!
Yeah that slipped my mind that there are four representations of the board in the physical version. I definitely realize it's better to have two boards instead of just one. (original assigned spec for this project was a single player version with one ship of five values). I've decided to move forward with the dual ship design like you suggested (it makes more sense).
I had tried to declare the method outside of the Main, but I wasn't able to access TheBoard
So I have created a GameBoard class, and then created TheBoard
namespace ConsoleBattle
{
public class Program
{
public static void Main(string[] args)
{
GameBoard playerA = new GameBoard();
GameBoard playerB = new GameBoard();
GameBoard playerAGuesses = new GameBoard();
GameBoard playerBGuesses = new GameBoard();
bool sunk = false;
string message;
Welcome();
Console.WriteLine("X: 0 - 9");
Console.WriteLine("Y: 0 - 9");
while (true)
{
Console.WriteLine(playerA.AddShip());
Console.ReadLine();
while(!sunk)
{
try
{
Console.WriteLine("Enter your guess: X, Y");
string guess = Console.ReadLine();
int xPos = Convert.ToInt32(guess.Split(',')[0]) - 1;
int yPos = Convert.ToInt32(guess.Split(',')[1]) - 1;
if (xPos > 9 || yPos > 9)
{
Console.WriteLine(message = "You are off the board, try again!");
}
playerA.CheckGuess(xPos, yPos);
}
catch
{
Console.WriteLine(message = "Unable to process coordinates");
}
Console.ReadLine();
}
Console.WriteLine(message = "You sunk the ship!");
Console.WriteLine("Play Again? [Y or N]");
string answer = Console.ReadLine().ToUpper();
if (answer == "Y")
{
continue;
}
else if (answer == "N")
{
return;
}
else
{
return;
}
}
}
private static void Welcome()
{
Console.WriteLine("Welcome to Console Battle!");
Console.WriteLine("Enter username: ");
string username = Console.ReadLine();
Console.WriteLine($"\nLet's begin {username} press Enter!");
Console.ReadLine();
}
}
}
When I call my playerA.CheckGuess(xPos, yPos) it is working as intended
in regards to getting the correct value or message back in the console.
I am able to enter my guess iteratively(which I was struggling with)
I have came up with a quasi solution for the ship assignment, but it only
seems to assign one ship value on the board when I wish to have five. Any
suggestions on how I can get five ships assigned?
Cheers for all the help so far,
namespace ConsoleBattle
{
public class GameBoard
{
public enum Square
{
Water,
Miss,
Ship,
Hit,
}
private Square[,] TheBoard = new Square[10, 10];
public Square[,] CheckGuess(int x, int y)
{
if (TheBoard[x, y] == Square.Ship)
{
Console.WriteLine(TheBoard[x, y] = Square.Hit);
}
else if (TheBoard[x, y] == Square.Water)
{
Console.WriteLine(TheBoard[x, y] = Square.Miss);
}
else if (TheBoard[x, y] == Square.Miss)
{
string message1 = "you already missed here";
Console.WriteLine(message1);
TheBoard[x, y] = Square.Miss;
}
else
{
string message2 = "this spot was hit already";
Console.WriteLine(message2);
TheBoard[x, y] = Square.Hit;
}
return TheBoard;
}
public Square[,] AddShip()
{
Random random = new();
string direction;
int x = random.Next(0, 9);
int y = random.Next(0, 9);
if (x < 4)
{
direction = "right";
}
else if (x > 4)
{
direction = "left";
}
else
{
direction = "left,right";
}
if (y < 4)
{
direction += "down";
}
else if (y > 4)
{
direction += "up";
}
else
{
direction += "up,down";
}
direction = direction.Split(',')[random.Next(0, direction.Split(',').Length)];
if (direction == "up")
{
for (int i = y; i < y - 4; i--)
{
TheBoard[i, y] = Square.Ship;
}
}
else if (direction == "down")
{
for (int i = y; i < y + 4; i++)
{
TheBoard[i, y] = Square.Ship;
}
}
else if (direction == "left")
{
for (int i = x; i < x - 4; i--)
{
TheBoard[x, i] = Square.Ship;
}
}
else
{
for (int i = x; i < x + 4; i++)
{
TheBoard[x, i] = Square.Ship;
}
}
return TheBoard;
}
}
}
|
|
|
|
|
OK. Let's get complicated ... but not too much, and it'll make life easier later.
Do you know what an abstract class is?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I have a surface level understanding of abstract classes. I know that it has to do with inheritance however I
have not used/implemented one before.
|
|
|
|
|
That's a good start.
OK, you have a board class, what do you put on the board?
In the real game, you don't add "single square" ships: you add 4 x Destroyer (two squares), 3 x submarines (three), 2 x Battleships (four) and 1 x Carrier (five).
So why not create an abstract base class Ship, which is inherited by the concrete classes Destroyer, Submarine, Battleship, and Carrier?
The abstract class has properties Length and Name which the concrete classes implement. The Name is so you can say "you sank my Destroyer!" without knowing which concrete class it is, and the Length says how many squares it takes.
Add a Add method to your board which accepts a Ship, a top left coordinate, and an orientation (NSEW) and modify your Hit method to see if it's sunk.
This way, the board doesn't need to know what ship it is, and it enforces rules like "you can't put it there because it hang over the side" and such like.
Have a think about it - I know it sounds complicated, but it's really pretty easy when you get the hang of it and it really does make your code easier to write and modify!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I've added the abstract Ship class within my Gameboard class below
Does it seem like it's headed in the right direction?
abstract class Ship
{
public string Name;
public int Length;
}
class Destroyer: Ship
{
}
class Submarine : Ship
{
}
class Cruiser : Ship
{
}
class Battleship : Ship
{
}
class Carrier : Ship
{
}
|
|
|
|
|
OK, those aren't properties: they are fields and it's considered bad practice to expose fields directly.
And the problems with fields are that they can be changed at any time, and your derived classes don't have to do anything with them - like giving them a value!
Creating abstract properties gets round both of those: the derived class must implement the property - which means it must return a relevant value - and it can be read only.
Here's my version of your code:
public abstract class Ship
{
public abstract string Name { get; }
public abstract int Length { get; }
}
public class Destroyer : Ship
{
public override string Name { get => "Destroyer"; }
public override int Length { get => 2; }
}
Because the properties are marked abstract , every time your derive a class from Ship it must implement it or your code won't compile!
Because I don't specify a setter in the abstract class property definition you can't add a setter to the derived class either, so the property cannot be changed by the outside world.
The derived class Destroyer "knows" how long it should be, and what the type of ship is called, so it implements that in the getter - the outside world doesn't need to know anything about it, the Destroyer class provides it as needed.
If you haven't seen this syntax before:
public override string Name { get => "Destroyer"; }
It's just a "short form" of this:
public override string Name
{
get
{
return "Destroyer";
}
}
Each of your derived classes implements it's own version, and the system uses the right one later:
private void MyButton_Click(object sender, EventArgs e)
{
Destroyer d1 = new Destroyer();
Cruiser c1 = new Cruiser();
ShowMe(d1);
ShowMe(c1);
}
private void ShowMe(Ship s)
{
Console.WriteLine($"A {s.Name} that is {s.Length} long.");
}
Prints:
A Destroyer that is 2 long.
A Cruiser that is 3 long.
You are letting Ships handle themselves instead of needing information to set be in your main code.
For example, if you wanted to add a helicopter class:
*
***
* You don't have to change your main code at all: it doesn't "need to know" that it's a weird shape, the new Helicopter class would deal with it, and it's just another Ship as far as your code is concerned:
public class Helicopter: Ship
{
public override string Name { get => "Helicopter"; }
public override int Length { get => 5; }
}
Does that make sense?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Yes that makes sense I can wrap my head around the concepts
I went through a couple of tutorials on abstract classes and getters/setters
along with applying them in Visual Studio
With your explanation of how the derived class(unique ships)
knows what propeties to take on without leaking the data
to the outside world it really simulates the blindness of
the physical game.
In the previous message you said:
"Add a Add method to your board which accepts a Ship, a top left coordinate, and an orientation (NSEW) and modify your Hit method to see if it's sunk."
From my understanding I'll be implementing something like this below:
playerA.PlaceShip(playerACruiser, int x,int y, Orientation.Vertical)
And then I will be implementing logic inside of the PlaceShip Method
to set them on the board?
You're helping me grow a ton and I really appreciate it
|
|
|
|
|
That's about it, but I'd probably simply it a bit.
There is a .NET struct that is really useful, called Point - which describes a ... well, a location in 2D space!
So the PlaceShip method signature would be
public Ship PlaceShip( Ship ship, Point loc, Orientation wayUp)
{
...
return ship;
} And I'd call it like this:
playerA.PlaceShip(new Cruiser(), new Point(x, y), Orientation.Vertical);
...
playerA.PlaceShip(new Destroyer(), new Point(x, y), Orientation.Vertical);
You seem to be doing pretty well!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
With the current setup to add the ship(s) with this method
public Ship PlaceShip(Ship ship, Point loc, Orientation direction)
{
for (int i = 0; i < ship.Length; i++)
{
}
Console.WriteLine(direction);
loc.x = 5;
loc.y = 5;
Console.WriteLine($"The {ship.Name} has a length of {ship.Length} squares");
return ship;
}
I understand how the ships will be created and passed in and I have
created the struct Point but don't quite know how I am going to
access values inside of it and pass them through as arguments
when PlaceShip is called. This is what I have so far and how I am
approaching it
public struct Point
{
public int x;
public int y;
public Coordinate(int x, int y)
{
this.x = x;
this.y = y;
}
}
Also I have created a class for Orientation and know that I need to
pass in either Horizontal or Vertical as an argument
public static int[] Vertical()
{
Console.WriteLine("I am Vertical");
return new int[5];
}
public static int[] Horizontal()
{
Console.WriteLine("I am Horizontal");
return new int[5];
}
|
|
|
|
|
You don't need to construct a Point class / struct - it's already in .NET: Point Struct (System.Drawing) | Microsoft Learn[^]
And why would you need two arrays? Does it matter once the ship is in place? Or is a Ship a collection of Point values that need to be hit?
Simplest solution is to use an Orientation enum and pass a value of that to your PlaceShip method:
private Random rand = new random();
...
Point loc = new Point (rand.Next(0, 10), rand.Next(0, 10));
Orientation orient = Enum.GetValues(typeof (Orientation))
.OfType<Orientation>()
.ElementAt(rand.Next(0, ));
Ship s = PlaceShip (new BattleShip, loc, orient); The code for a random enum value may be unfamiliar to you: it uses generics (which you may not have met yet) and Linq methods (which you may not have met either) to do the work:
Gets an array of all possible values of the enum
Cast the array to a more useful type - a collection of Orientation values.
Select a random element.
I could have written it like this:
orient = (Orientation)rand.Next(0, 4); Which gives the same result - but it assumes that the orientation values are sequential: if you want to add diagonals later there are good reasons for making your enum values non-sequential:
public enum Orientation
{
Up = 1,
Down = 2,
Left = 4,
Right = 8,
} Because now you can combine values to form "Up and Right" without changing working code.
orient = Orientation.Up | Orientation.Right; Does that make sense?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I understand the creation and passing of the Enum for the orientation
From what I have searched and found for the Point struct/class:
I was not able to instantiate/access this from the foundational .NET suite
So I looked up how to create my own Point struct(Struct in C#[^]),
and tried to access it like that
Maybe there is a better way to access the Point struct/class?
Also I am having a difficult time accessing classes, properties,
and methods across my two files GameBoard.cs and Program.cs
Program.cs
namespace ConsoleBattle
{
public class Program
{
public static void Main(string[] args)
{
GameBoard playerA = new GameBoard();
GameBoard playerAGuesses = new GameBoard();
bool sunk = false;
string message;
Welcome();
Console.WriteLine("X: 0 - 9");
Console.WriteLine("Y: 0 - 9");
while (true)
{
playerA.PlaceShip(new Submarine(),new Point(x,y), Orientation.Down);
while (!sunk)
{
try
{
Console.WriteLine("Enter your guess: X, Y");
string guess = Console.ReadLine();
int xPos = Convert.ToInt32(guess.Split(',')[0]) - 1;
int yPos = Convert.ToInt32(guess.Split(',')[1]) - 1;
if (xPos > 9 || yPos > 9)
{
Console.WriteLine(message = "You are off the board, try again!");
}
playerA.CheckGuess(xPos, yPos);
}
catch
{
Console.WriteLine(message = "Unable to process coordinates");
}
Console.ReadLine();
}
Console.WriteLine(message = "You sunk the ship!");
Console.WriteLine("Play Again? [Y or N]");
string answer = Console.ReadLine().ToUpper();
if (answer == "Y")
{
continue;
}
else if (answer == "N")
{
return;
}
else
{
return;
}
}
}
private static void Welcome()
{
Console.WriteLine("Welcome to Console Battle!");
Console.WriteLine("Enter username: ");
string username = Console.ReadLine();
Console.WriteLine($"\nLet's begin {username} press Enter!");
Console.ReadLine();
}
}
}
|
|
|
|
|
GameBoard.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleBattle
{
public class GameBoard
{
public enum Square
{
Water,
Miss,
Ship,
Hit,
}
private Square[,] TheBoard = new Square[10, 10];
public Square[,] CheckGuess(int x, int y)
{
if (TheBoard[x, y] == Square.Ship)
{
Console.WriteLine(TheBoard[x, y] = Square.Hit);
}
else if (TheBoard[x, y] == Square.Water)
{
Console.WriteLine(TheBoard[x, y] = Square.Miss);
}
else if (TheBoard[x, y] == Square.Miss)
{
string message1 = "you already missed here";
Console.WriteLine(message1);
}
else
{
string message2 = "this spot was hit already";
Console.WriteLine(message2);
}
return TheBoard;
}
public Ship PlaceShip(Ship ship, Point loc, Orientation wayUp)
{
Console.WriteLine(ship.Name);
Console.WriteLine(wayUp);
Console.WriteLine(loc);
return ship;
}
}
public enum Orientation
{
Up = 1,
Down = 2,
Left = 4,
Right = 8,
}
public struct Point
{
public int x;
public int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
public abstract class Ship
{
public abstract string Name { get; }
public abstract int Length { get; }
}
public class Destroyer : Ship
{
public override string Name { get => "Destroyer"; }
public override int Length { get => 2; }
}
public class Submarine : Ship
{
public override string Name { get => "Submarine"; }
public override int Length { get => 3; }
}
public class Cruiser : Ship
{
public override string Name { get => "Cruiser"; }
public override int Length { get => 3; }
}
public class Battleship : Ship
{
public override string Name { get => "Battleship"; }
public override int Length { get => 4; }
}
public class Carrier : Ship
{
public override string Name { get => "Carrier"; }
public override int Length { get => 5; }
}
}
It has gotten pretty messy and I am feeling a little frustrated
Any suggestions on what I can do to clean it up?
This must be one of the longest threads ever
|
|
|
|
|
Hi Griff,
Hope you are doing well
I am up to speed with everything you mentioned on this post
I figured out I needed the using directive System.Drawing in order
to access the built in .NET Point struct
With that said I successfully implemented the randomization of
Point loc, and the Orienation orient (represented as Enums)
I am able to call the PlaceShip method on the object playerA
and create the 1 ship, 2 location, and 3 orientation as follows:
playerA.PlaceShip(new Submarine(), loc, orient);
...
public Ship PlaceShip(Ship ship, Point loc, Orientation wayUp)
{
loc = (x, y);
string direction;
int x;
int y;
if (wayUp == Orientation.Right)
{
direction = "right";
}
else if (wayUp == Orientation.Left)
{
direction = "left";
}
else
{
direction = "left,right";
}
if (wayUp == Orientation.Down)
{
direction += "down";
}
else if (wayUp == Orientation.Up)
{
direction += "up";
}
else
{
direction += "up,down";
}
if (direction == "up")
{
for (int i = y; i < y - 4; i--)
{
TheBoard[i, y] = Square.Ship;
}
}
else if (direction == "down")
{
for (int i = y; i < y + 4; i++)
{
TheBoard[i, y] = Square.Ship;
}
}
else if (direction == "left")
{
for (int i = x; i < x - 4; i--)
{
TheBoard[x, i] = Square.Ship;
}
}
else
{
for (int i = x; i < x + 4; i++)
{
TheBoard[x, i] = Square.Ship;
}
}
Console.WriteLine(ship.Name);
Console.WriteLine(wayUp);
Console.WriteLine(loc);
return ship;
}
In the console when I run the program with PlaceShip() above I get back the following:
Submarine
Left
{X=7, Y=1}
What I am working on now is placement of the Ship(s)
inside the PlaceShip Method:
I am trying to acces the x, y coordinates that come from Point loc
within the PlaceShip Method, and them use them in the assignment
logic with the looping and if/else statements in order to populate
the board with the Enum Ship
I know that I need to take the location and orientation and loop
over them to populate TheBoard with the Enum Ship, but having
a hard implementing that functionality.
|
|
|
|
|
Hi! I haven't forgotten you - I've just been really, really busy for the last week, and it doesn't look like it'll improve till the weekend at least.
I will get back to you, but I don't want to hurry what I say and either send you off on a wrong tangent or confuse you - and that takes time to review the whole conversation we've had and plan out how best to help you next!
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Okay no worries at all I completely understand and appreciate the valuable time you spend while helping me.
I'll continue to try and move forward, and watch/code C# tutorials since there is what seems like a plethora of concepts to cover in .NET
|
|
|
|
|
Hope all is well
So I am able to pass in all three arguments to the PlaceShip method
And I am copying the Point loc inside of of the method and assigning them values of loc.X, loc.Y,
so I can use them in my loops in my conditional statements (not sure if that's the best way)
I am trying to place and populate the ship with the Enum Ship
Sometimes the values get filled, but other times I am off the board or the logic won't execute. I can see
the values get populated on TheBoard when I step through it while debugging.
This is what it looks like currently:
public Ship PlaceShip(Ship ship, Point loc, Orientation direction)
{
loc = new Point(loc.X, loc.Y);
Console.WriteLine(loc);
if (direction == Orientation.Up)
{
for (int i = loc.Y; i < loc.Y - 5; i--)
{
TheBoard[loc.X, i] = Square.Ship;
}
}
else if (direction == Orientation.Down)
{
for (int i = loc.Y; i < loc.Y + 5; i++)
{
TheBoard[loc.X, i] = Square.Ship;
}
}
else if (direction == Orientation.Left)
{
for (int i = loc.X; i < loc.X - 5; i--)
{
TheBoard[i, loc.Y] = Square.Ship;
}
}
else
{
for (int i = loc.X; i < loc.X + 5; i++)
{
TheBoard[i, loc.Y] = Square.Ship;
}
}
Console.WriteLine(ship.Name);
Console.WriteLine(direction);
return ship;
}
}
|
|
|
|
|
In terms of randomly populating, you'd have to tally the rows and columns that are available "before" each "chance" throw; whose range would depends on the # of available "slots".
A "5 cell" ship on a 10 x 10 grid would obviously only fit in a row or column that had 5 "contiguous" empty cells; something that could be figured out by iterating over the array before each throw. Use the "id #" of the ship to populate the grid cells and identify what got hit. Another throw could determine the offset within the column / row if more than 5 cells are available for "slotting".
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
Hi. I'm working with Visual Studio on a application that changes a windows registry. There is a small forms that requests an address in a text box. The contents of the text box wil be writen is the registry. I need the key to be LocalMachine and here is a snippet of this code:
private void button1_Click(object sender, EventArgs e)
{
RegistryKey chaveBase, chave;
chaveBase = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
chave = chaveBase.CreateSubKey("Software\\Antares", true);
chave.SetValue("Endereço", textBox1.Text);
}
The problem is that I receive the message: "System.UnauthorizedAccessException: 'Access to the registry key 'HKEY_LOCAL_MACHINE\Software\Antares' is denied".
If I change LocalMachine for CurrentUser, it works fine. What should I do to access LocalMachine?
|
|
|
|
|
This is because you have to be running the code as an administrator to make any changes to anything under HKEY_LOCAL_MACHINE.
Hold down a Shift key, right-click your executable and click "Run as administrator". You'll, of course, have to supply admin credentials, or if you're already using an admin account, you'll have to OK using the admin part of your account to run the app. Then it'll work.
|
|
|
|
|