7
\$\begingroup\$

This is my first attempt at anything in C#.

It's based on battleships, but there's only one player and only one ship.

The gameplay is super simple: just try to find the ship... keep guessing until you've found it.

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

namespace FirstApplication
{
    class Battleship
    {
        public int shipPosition;
        public string board = "-------------\n| 1 | 2 | 3 |\n-------------\n| 4 | 5 | 6 |\n-------------\n| 7 | 8 | 9 |\n-------------";
        public bool shipIsHit = false;

        public void PositionShip()
        {
            Random randomNumber = new Random();
            shipPosition = randomNumber.Next(1, 10); // random number between 1 and 9
        }
        public void HitPosition(int hit)
        {
            if (hit == shipPosition)
            {
                board = board.Replace(hit.ToString(),"X");
                shipIsHit = true;
            }
            else
            {
                board = board.Replace(hit.ToString(), "-");
            }
        }  
    }
    class Program
    {
        static void Main(string[] args)
        {
            Battleship game = new Battleship();
            int guess;
            bool isInteger;

            game.PositionShip();

            Console.WriteLine("You are playing battleship... Try to find the ship!");

            do
            {
                Console.WriteLine("\n\nEnter a number between 1 and 9");
                Console.WriteLine("Board: \n{0}", game.board);

                isInteger = Int32.TryParse(Console.ReadLine(), out guess);

                if (isInteger && (guess >= 1 && guess <= 9))
                { 
                    game.HitPosition(guess);
                }
                else
                {
                    Console.WriteLine("That's not a valid number!");
                }

                if (!game.shipIsHit)
                {
                    Console.WriteLine("You missed!");
                }
            }
            while (!game.shipIsHit);

            Console.WriteLine("\n\nBoard: \n{0}", game.board);
            Console.WriteLine("BOOM! You found the battleship at position {0}", game.shipPosition);
            Console.ReadKey();
        }
    }
}

I'm looking forward adding more functionality as I progress. If you have any suggestions I'd be interested to hear them too!

\$\endgroup\$
  • \$\begingroup\$ Console.WriteLine("You missed!"); repeats on invalid number \$\endgroup\$ – paparazzo Mar 23 '18 at 17:09
  • \$\begingroup\$ @paparazzo I kinda did that by design. I'm a harsh gamesmaster and plan to penalise the user for typing in wrong values in future iterations of the game i.e. move straight to opponents turn. I guess in this version of the game it doesn't make as much sense since you just output all the same stuff again. \$\endgroup\$ – CallumDA Mar 23 '18 at 17:15
4
\$\begingroup\$

Sorry, wrong number

  • Penaly - it could be a good faith typo. And the valid range should be displayed w/ the error message.

Edit

This edit is about moving functionality out of Main() and into Battleship class where it belongs. The themes are rigorous encapsulation, using game terminology, and Single Responsibility Principle.


Battleship.Fire()

Battleship has the ocean map so it makes sense it should be the one checking for "in range" shots.

public string Fire(int shot) {
   if(!InRange(shot) return string.Format("Shot is over-board. Grid range is {0} - {1}", game.lowLimit, game.highLimit);
   if(!IsHit(shot) return "You missed!";
   return "It's a hit!";
}

Battleship.InRange()

protected bool InRange(int shot) { 
   return (shot >= lowLimit && shot <= highLimit);
}

// set in constructor, let's say
public int lowLimit {get, protected set};
public int highLimit { get, protected set} ;

protected bool IsHit(int shot) {
   shipIsHit =  shot == shipPosition;
   return shipIsHit;
}

The do while loop:

 do
 {
     Console.WriteLine("\n\nEnter a number between {0} and {1}", game.lowLimt, game.highLimit);
     Console.WriteLine("Board: \n{0}", game.board);

     Console.WriteLine(game.Fire(Console.ReadLine()));

 }
 while (!game.shipIsHit);

Maybe Fire() should return something that has both a 'is hit' boolean and a user message. Maybe an out parameter will suffice. I really do not like that the driver has to know about an internal state property.

Internal to Battleship: Create a Board class to wrap up all those lose board-specific properties and give them context.

Note the evolving structure

The code is simpler overall! Fire() reads very high level and it's simple too, nice! This is the invisible hand of OO programming at work.

end Edit


Now What?

Where/what do you want this to evolve to? Not much else to say w/out that vision. Nonetheless here's my "first thoughts":

Hit vs Sunk

The do while should end with the ship sunk. This is a good place to start evolving the code because it addresses the core state management of the game. Getting core fundamentals right profoundly effects the entire app code and structure. And "hit vs sunk" necessarily motivates structural changes of the game's objects. My immediate thought is the ship itself.

Firing

Encapsulate the random number generation into a method that exposes the idea of firing the gun.

I think there is no point of a particular ship firing. That may be obvious from actually playing "battleship" but these sorts of things must be explicit.

Ship Class

Will have to know how many hits it can take and how many it has taken. The fact that a ship has 1 or 20 "hit points" should not be exposed in the loop or anywhere else. The ship may know where it is in the ocean (every ship has a navigator doesn't it?) - so it can tell if it got hit.


Notice the evolving code structure

Method calls and game objects should read like "we're playing battleship". The do while will not ask "is it an integer?" but only "was the ship hit?"

Class Battleship will start to look and behave like the games' driver - it is the game. Main() will do little more than kickstart the game.

Everything above is the start of desirable decoupling thus enabling further code evolution. For example the Battlship class will not lose half its functionality because Main() has it. And the firing loop logic will be stable w/out having to deal with minutia that is encapsulated in the Ship class - and the entire Fleet; more classes to come ...!

\$\endgroup\$
  • \$\begingroup\$ Thank you so much for this, it's been a huge help! Coming from VBA I knew the OO side of things would be where I struggle initially, so great to get some proper feedback there! I'm already working on my next project and really trying to improve encapsulation and SRP. \$\endgroup\$ – CallumDA Mar 27 '18 at 13:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.