1
\$\begingroup\$

I made a simple battleship in C# console with a little AI ( just a little ) and The goal of this practice is improving my knowledge about OOP.

Game Rules:

1.You Attack To The Enemy then he Attacked You.

2.if you do not find any ships with the help of Hint, one of your own ships will sink

3.I Write a Simple Little Small Tiny AI. If Enemy can't find your ship in his turn he can search near and find the ships for next turn (if it is near the ship).

4.You and your enemy just have 5 ships.

First i put all of my code then explain about it.

My Program Class [Main Method]:

    {
        static void Main()
        {

            GameEngine myGameEngine = new GameEngine();

            while (true)
            {
                myGameEngine.Engine();
            }
        }
    }

it's very simple and clean.

Then gameEngine Class:

class GameEngine
    {
        List<char> _myIsland = new List<char>();
        List<char> _enemyIsland = new List<char>();
        List<char> _enemyIslandCover = new List<char>();
        bool isGenerate;
        int hint;
        int cubePosition;
        char saveCharacter;
        private Ships ships;

        enum Direction
        {
            Up = -10,
            Left = -1,
            Right = 1,
            Down = 10
        }

        public GameEngine()
        {
            ships = new Ships(this, 5);
            hint = 1;
        }

        public void DrawIslands()
        {
            int Counter = 0;
            int HowManyTime = 0;
            Console.Clear();

            while (true)
            {
                HowManyTime += 10;
                while (Counter < HowManyTime)
                {
                    Console.Write("{0} ", _myIsland[Counter]);
                    Counter++;
                }

                Console.Write("║ ");
                Counter -= 10;

                while (Counter < HowManyTime)
                {
                    Console.Write("{0} ", _enemyIslandCover[Counter]);
                    Counter++;
                }

                Console.WriteLine();

                if (HowManyTime == 100)
                {
                    break;
                }
            }
        }

        private void ShowInformation()
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("░ YOUR SHIPS: {0} \t ENEMY's SHIP: {1} \t HINT: {2}", ships.YourShip, ships.EnemyShip, hint);
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine("░ 1. ATTACK \t 2. HINT \t 3. EXIT");
            Console.ForegroundColor = ConsoleColor.White;
        }

        public void Engine()
        {
            if (!isGenerate)
            {
                FirstTimeGenerate();
            }
            DrawIslands();
            ShowInformation();
            string userInput = GetString("░ Choose a number > ");

            switch (userInput)
            {
                case "1":
                    cubePosition = 0;
                    saveCharacter = _enemyIslandCover[cubePosition];
                    _enemyIslandCover[cubePosition] = '■';
                    DrawIslands();
                    bool isEnter = false;
                    
                    while (!isEnter)
                    {
                        ConsoleKeyInfo readKey = Console.ReadKey();

                        switch (readKey.Key)
                        {
                            case ConsoleKey.UpArrow:
                                Move(Direction.Up);
                                break;

                            case ConsoleKey.DownArrow:
                                Move(Direction.Down);
                                break;

                            case ConsoleKey.RightArrow:
                                Move(Direction.Right);
                                break;

                            case ConsoleKey.LeftArrow:
                                Move(Direction.Left);
                                break;

                            case ConsoleKey.Enter:
                                isEnter = true;
                                bool result = ships.Attack(_enemyIsland, _enemyIslandCover , cubePosition);

                                if (result)
                                {
                                    GetSucces("You Sink One of the Enemy's Ship!! POWER");
                                    if (hint == 0)
                                    {
                                        hint++;
                                    }
                                    HasAnyoneWon();
                                }
                                else
                                {
                                    GetError("DAMN IT ADMIRAL!!!");
                                }
                                break;
                        }
                    }

                    ships.EnemyAttack(_myIsland);
                    HasAnyoneWon();

                    break;

                case "2":
                    if (hint == 1)
                    {
                        string userChoice = GetString("Are you sure for use hint [PRESS Y]? (if there is no ship in row, One of your ship will sink)");
                        if (userChoice == "y")
                        {
                            bool result = ships.Hint(_enemyIsland, _enemyIslandCover);
                            hint = 0;
                            if (!result)
                            {
                                for (int i = 0; i < 3; i++)
                                {
                                    int index = _myIsland.LastIndexOf('@');
                                    _myIsland.RemoveAt(index);
                                    _myIsland.Insert(index, '░');
                                }
                                DrawIslands();
                                HasAnyoneWon();
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        GetError("You have no any hint.");
                    }
                    break;

                case "3":
                    Console.Clear();
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine("Thank you for choose my game. See you later");
                    Console.ForegroundColor = ConsoleColor.White;
                    Environment.Exit(0);
                    break;

                default:
                    GetError("You Put Wrong Value!");
                    break;
            }
        }

        private void FirstTimeGenerate()
        {
            for (int i = 0; i < 100; i++)
            {
                _myIsland.Add('■');
                _enemyIsland.Add('■');
                _enemyIslandCover.Add('·');
            }
            ships.GenerateShips(_myIsland);
            ships.GenerateShips(_enemyIsland);
            isGenerate = true;
        }

        private void Move(Direction dir)
        {
            _enemyIslandCover[cubePosition] = saveCharacter;
            cubePosition += (int)dir;

            if (cubePosition > 0 && cubePosition < 100)
            {
                saveCharacter = _enemyIslandCover[cubePosition];
                _enemyIslandCover[cubePosition] = '■';
                DrawIslands();
            }
            else
            {
                GetError("You Can't Move Outer the Field");
                cubePosition = 0;
            }
        }

        private void HasAnyoneWon()
        {
            if (ships.EnemyShip == 0 || ships.YourShip == 0)
            {
                int Counter = 0;
                int HowManyTime = 0;
                Console.Clear();

                while (true)
                {
                    HowManyTime += 10;
                    while (Counter < HowManyTime)
                    {
                        Console.Write("{0} ", _myIsland[Counter]);
                        Counter++;
                    }

                    Console.Write("║ ");
                    Counter -= 10;

                    while (Counter < HowManyTime)
                    {
                        Console.Write("{0} ", _enemyIsland[Counter]);
                        Counter++;
                    }

                    Console.WriteLine();

                    if (HowManyTime == 100)
                    {
                        break;
                    }
                }

                if (ships.EnemyShip == 0)
                {
                    GetSucces("YOU WON ADMIRAL! WE BACK TO THE COUNTRY AND TELL TO PEOPLE ABOUT YOUR BRAVE!!!! HOOOOOOOOOORAY");
                }
                else
                {
                    GetError("YOU LOOSE ADMIRAL! DON'T BE UPSET. PLAY GAME AGAIN!");
                }

                Environment.Exit(0);
            }
        }

        // STATIC METHOD - ONCE WRITE EVERYWHERE USE
        public static string GetString(string message)
        {
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.Write(message.ToLower());
            Console.ForegroundColor = ConsoleColor.White;
            return Console.ReadLine();
        }

        public static void GetError(string message)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(message);
            Console.ForegroundColor = ConsoleColor.White;
            Thread.Sleep(3000);
        }

        public static void GetSucces(string message)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(message);
            Console.ForegroundColor = ConsoleColor.White;
            Thread.Sleep(2000);
        }
    }

And my last class named Ships because this class generate and create ships and all of things about ships :

class Ships
    {
        public int YourShip { get; set; }
        public int EnemyShip { get; set; }
        int indexOfAI = 0;
        private GameEngine gameEnigne;

        public Ships(GameEngine gameEngine, int howManyShip)
        {
            this.gameEnigne = gameEngine;
            YourShip = howManyShip;
            EnemyShip = howManyShip;
        }

        public void GenerateShips(List<char> island)
        {
            int counter = 0;
            Random randomGenerator = new Random();
            while (true)
            {
                if (counter == YourShip)
                {
                    break;
                }

                int index = randomGenerator.Next(0, island.Count);

                if (FindBestPlace(index, island))
                {
                    island[index] = '@';
                    island[index - 1] = '@';
                    island[index + 1] = '@';

                    counter++;
                }
                else
                {
                    continue;
                }
            }
        }

        private bool FindBestPlace(int index, List<char> island)
        {
            try
            {
                int Counter = index - (index % 10);
                for (int i = Counter; i < Counter + 10; i++)
                {
                    if (island[i] != '■')
                    {
                        return false;
                    }
                }
                if (index % 10 > 2 && index % 10 < 9)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch
            {
                return false;
            }
        }

        public bool Hint(List<char> enemyIsland, List<char> enemyIslandCover)
        {
            Random rnd = new Random();
            int index = 0;
            bool sunkenShip = false;
            bool thereIsAnyShip = false;

            while (!sunkenShip)
            {
                index = enemyIsland.Count / 10 * (rnd.Next(0, 10));

                sunkenShip = true;
                thereIsAnyShip = false;

                for (int i = index; i < index + 10; i++)
                {
                    if (enemyIsland[i] == '░')
                    {
                        sunkenShip = false;
                        break;
                    }
                    if (enemyIsland[i] == '@')
                    {
                        thereIsAnyShip = true;
                    }
                }
            }

            for (int i = index; i < index + 10; i++)
            {
                enemyIslandCover[i] = enemyIsland[i];
            }
            gameEnigne.DrawIslands();
            Thread.Sleep(1000);
            for (int i = index; i < index + 10; i++)
            {
                enemyIslandCover[i] = '·';
            }
            gameEnigne.DrawIslands();

            if (!thereIsAnyShip)
            {
                YourShip--;
            }

            return thereIsAnyShip;
        }

        public bool Attack(List<char> enemyIsland, List<char> enemyIslandCover, int cubePosition)
        {
            if (enemyIsland[cubePosition] == '@')
            {
                int rowIndex = cubePosition - (cubePosition % 10);
                for (int i = rowIndex; i < rowIndex + 10; i++)
                {
                    if (enemyIsland[i] == '@')
                    {
                        enemyIsland[i] = '░';
                        enemyIslandCover[i] = '░';
                    }
                }
                gameEnigne.DrawIslands();
                EnemyShip--;

                return true;
            }
            else
            {
                enemyIslandCover[cubePosition] = '%';
                return false;
            }

        }

        public void EnemyAttack(List<char> MyIsland)
        {
            GameEngine.GetError("It's Turn of enemy | HIDE HIDE HIIIIIIIDE !!!! ");

            int index;
            Random rnd = new Random();
            if (indexOfAI > 0)
            {
                if (indexOfAI < 70)
                {
                    index = rnd.Next(indexOfAI, indexOfAI + 30);
                }
                else
                {
                    index = rnd.Next(indexOfAI, MyIsland.Count);
                }
            }
            else
            {
                index = rnd.Next(0, MyIsland.Count);
            }

            char saveChar = MyIsland[index];
            MyIsland[index] = 'Ö';
            gameEnigne.DrawIslands();
            GameEngine.GetError("Enemy Choose His Location!! DON'T MOVE!! Sheeeeesh");

            MyIsland[index] = saveChar;

            bool isThereAnyShip = false;

            if (MyIsland[index] == '@')
            {
                int Counter = index - (index % 10);

                for (int i = Counter; i < Counter + 10; i++)
                {
                    if (MyIsland[i] == '@')
                    {
                        MyIsland[i] = '░';
                        isThereAnyShip = true;
                    }
                }
            }

            gameEnigne.DrawIslands();
            if (isThereAnyShip)
            {
                YourShip--;
                GameEngine.GetError("Enemy Sink One Of Your Ship Admiral, What's The Next Order ?");
                indexOfAI = 0;
            }
            else
            {
                GameEngine.GetSucces("The danger was eliminated. The enemy does not know the location of our ship!!");
                if (indexOfAI == 0)
                {
                    SimpleEnemyAI(index, MyIsland);
                }
                else
                {
                    indexOfAI = 0;
                }
            }  
        }

        private void SimpleEnemyAI(int index , List<char> Island)
        {
            // it's a algorithm for Simple AI
            if (index > 10)
            {
                index = index - (index % 10) - 10;
            }
            bool isAnyShipFind = false;

            if (index < 70)
            {
                for (int i = index; i < index + 20; i++)
                {
                    if (Island[i] == '@')
                    {
                        indexOfAI = index;
                        isAnyShipFind = true;
                        break;
                    }
                }
            }
            else
            {
                for (int i = index; i < Island.Count; i++)
                {
                    if (Island[i] == '@')
                    {
                        indexOfAI = index;
                        isAnyShipFind = true;
                        break;
                    }
                }
            }

            if (isAnyShipFind)
            {
                GameEngine.GetError("But Careful! Enemy Have Radar And He Saw Vague Things In It!!");
            }
            else
            {
                indexOfAI = 0;
            }
        }
    }

Explain:

first i create a class and named it GameEngine and Create a object from Ships Class and leave it NULL and create a constructor for it:

        private Ships ships;

        public GameEngine()
        {
            ships = new Ships(this, 5);
            hint = 1;
        }

i refrenced ships to a new Ship and send to it this Class as a object (i send exactly this game engine refrence and didn't create a new GameEngine because if i create a new GameEngine object then i can't access to the filled lists)

with this way ( thanks again from @aepot) i can access to the Ships Member And send a GameEngine object to the Ships Class and magicly i can access to the GameEngine Members in the ships class because i send a object from GameEngine to the Ships class. then i send 5 instead dynamic parameter to the Ships Constructor. That number is about the number of enemy ships and ourselves ( I could have made it dynamic, but I focused more on the game so i send a const number like 5 ) see:

        public int YourShip { get; set; }
        public int EnemyShip { get; set; }
        int indexOfAI = 0;
        private GameEngine gameEnigne;

        public Ships(GameEngine gameEngine, int howManyShip)
        {
            this.gameEnigne = gameEngine;
            YourShip = howManyShip;
            EnemyShip = howManyShip;
        }

i explain about indexOfAI.

i made three list. one of them is my Island or sea. One of them is enemy's Island or sea. And the last one is enemy's island but i covered it by dot because we haven't any permission to see the Enemy's island with details.

        List<char> _myIsland = new List<char>();
        List<char> _enemyIsland = new List<char>();
        List<char> _enemyIslandCover = new List<char>();

then i write a FirstTimeGenerate Method And Call it in to the Engine Method:

private void FirstTimeGenerate()
        {
            for (int i = 0; i < 100; i++)
            {
                _myIsland.Add('■');
                _enemyIsland.Add('■');
                _enemyIslandCover.Add('·');
            }
            ships.GenerateShips(_myIsland);
            ships.GenerateShips(_enemyIsland);
            isGenerate = true;
        }

in the FirstTimeGenerate you see GenerateShips Method.This method and FindBestPlace put ships in the island or sea:

        public void GenerateShips(List<char> island)
        {
            int counter = 0;
            Random randomGenerator = new Random();
            while (true)
            {
                if (counter == YourShip)
                {
                    break;
                }

                int index = randomGenerator.Next(0, island.Count);

                if (FindBestPlace(index, island))
                {
                    island[index] = '@';
                    island[index - 1] = '@';
                    island[index + 1] = '@';

                    counter++;
                }
                else
                {
                    continue;
                }
            }
        }

FindBestPlaceMethod:

        private bool FindBestPlace(int index, List<char> island)
        {
            try
            {
                int Counter = index - (index % 10);
                for (int i = Counter; i < Counter + 10; i++)
                {
                    if (island[i] != '■')
                    {
                        return false;
                    }
                }
                if (index % 10 > 2 && index % 10 < 9)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch
            {
                return false;
            }
        }

FindBestPlace Search in the island and generate random number if the number of row is empty ( there is no ship in row) then create a ship otherwise search again and generate number again.

after that i create method for Draw the island and show information like user Choice and Score etc.

         public void DrawIslands()
        {
            int Counter = 0;
            int HowManyTime = 0;
            Console.Clear();

            while (true)
            {
                HowManyTime += 10;
                while (Counter < HowManyTime)
                {
                    Console.Write("{0} ", _myIsland[Counter]);
                    Counter++;
                }

                Console.Write("║ ");
                Counter -= 10;

                while (Counter < HowManyTime)
                {
                    Console.Write("{0} ", _enemyIslandCover[Counter]);
                    Counter++;
                }

                Console.WriteLine();

                if (HowManyTime == 100)
                {
                    break;
                }
            }
        }

        private void ShowInformation()
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("░ YOUR SHIPS: {0} \t ENEMY's SHIP: {1} \t HINT: {2}", 
            ships.YourShip, ships.EnemyShip, hint);
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine("░ 1. ATTACK \t 2. HINT \t 3. EXIT");
            Console.ForegroundColor = ConsoleColor.White;
        }

In this algorithm, first 10 Steps from our own sea are displayed, then a distance and 10 Steps from the enemy's sea (note that in the enemy's sea we only see points because we have no right to access the location of the enemy ship)

then i wrote Hint Method but first look at engine of hint:

               case "2":
                    if (hint == 1)
                    {
                        string userChoice = GetString("Are you sure for use hint [PRESS Y]? (if 
                        there is no ship in row, One of your ship will sink)");
                        if (userChoice == "y")
                        {
                            bool result = ships.Hint(_enemyIsland, _enemyIslandCover);
                            hint = 0;
                            if (!result)
                            {
                                for (int i = 0; i < 3; i++)
                                {
                                    int index = _myIsland.LastIndexOf('@');
                                    _myIsland.RemoveAt(index);
                                    _myIsland.Insert(index, '░');
                                }
                                DrawIslands();
                                HasAnyoneWon();
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        GetError("You have no any hint.");
                    }
                    break;

Hint Method:

        public bool Hint(List<char> enemyIsland, List<char> enemyIslandCover)
        {
            Random rnd = new Random();
            int index = 0;
            bool sunkenShip = false;
            bool thereIsAnyShip = false;

            while (!sunkenShip)
            {
                index = enemyIsland.Count / 10 * (rnd.Next(0, 10));

                sunkenShip = true;
                thereIsAnyShip = false;

                for (int i = index; i < index + 10; i++)
                {
                    if (enemyIsland[i] == '░')
                    {
                        sunkenShip = false;
                        break;
                    }
                    if (enemyIsland[i] == '@')
                    {
                        thereIsAnyShip = true;
                    }
                }
            }

            for (int i = index; i < index + 10; i++)
            {
                enemyIslandCover[i] = enemyIsland[i];
            }
            gameEnigne.DrawIslands();
            Thread.Sleep(1000);
            for (int i = index; i < index + 10; i++)
            {
                enemyIslandCover[i] = '·';
            }
            gameEnigne.DrawIslands();

            if (!thereIsAnyShip)
            {
                YourShip--;
            }

            return thereIsAnyShip;
        }

if user Choose 2 Hint Method Run But first i put some expression for user: first of all check user have hint or not. then i ask a question are you sure for use your hint, because if you use it and don't find any ship you lose your ship. then i send List of Enemy island and list of cover. in Hint method, a random number will be create and check if the row of random number have a sink ship again search for the new row and create new random number.

then if you press 1 and if it's your turn you can attack

                    case "1":
                    cubePosition = 0;
                    saveCharacter = _enemyIslandCover[cubePosition];
                    _enemyIslandCover[cubePosition] = '■';
                    DrawIslands();
                    bool isEnter = false;
                    
                    while (!isEnter)
                    {
                        ConsoleKeyInfo readKey = Console.ReadKey();

                        switch (readKey.Key)
                        {
                            case ConsoleKey.UpArrow:
                                Move(Direction.Up);
                                break;

                            case ConsoleKey.DownArrow:
                                Move(Direction.Down);
                                break;

                            case ConsoleKey.RightArrow:
                                Move(Direction.Right);
                                break;

                            case ConsoleKey.LeftArrow:
                                Move(Direction.Left);
                                break;

                            case ConsoleKey.Enter:
                                isEnter = true;
                                bool result = ships.Attack(_enemyIsland, _enemyIslandCover , 
                                cubePosition);

                                if (result)
                                {
                                    GetSucces("You Sink One of the Enemy's Ship!! POWER");
                                    if (hint == 0)
                                    {
                                        hint++;
                                    }
                                    HasAnyoneWon();
                                }
                                else
                                {
                                    GetError("DAMN IT ADMIRAL!!!");
                                }
                                break;
                        }
                    }

                    ships.EnemyAttack(_myIsland);
                    HasAnyoneWon();

                    break;

at the first i put the cursor or point on the first of enemy's sea ( On Zero ). then you can move the curser real time with this method and enum:

Enum :

        enum Direction
        {
            Up = -10,
            Left = -1,
            Right = 1,
            Down = 10
        }

Up = -10 means if you choose up cursor move -10 step back on the list and ...

Move Method:

        private void Move(Direction dir)
        {
            _enemyIslandCover[cubePosition] = saveCharacter;
            cubePosition += (int)dir;

            if (cubePosition > 0 && cubePosition < 100)
            {
                saveCharacter = _enemyIslandCover[cubePosition];
                _enemyIslandCover[cubePosition] = '■';
                DrawIslands();
            }
            else
            {
                GetError("You Can't Move Outer the Field");
                cubePosition = 0;
            }
        }

in this method i save the next house character that the square is going to occupy in saveCharacter variable, which I previously defined outside of this method and in the same class.

then if you press enter you can attack on the square house:

                           case ConsoleKey.Enter:
                                isEnter = true;
                                bool result = ships.Attack(_enemyIsland, _enemyIslandCover , 
                                cubePosition);

                                if (result)
                                {
                                    GetSucces("You Sink One of the Enemy's Ship!! POWER");
                                    if (hint == 0)
                                    {
                                        hint++;
                                    }
                                    HasAnyoneWon();
                                }
                                else
                                {
                                    GetError("DAMN IT ADMIRAL!!!");
                                }
                                break;

i send lists of enemy and cube position ( index of cube ). in the attack method i wrote a algorithm. if i exactly shot on the ship then ship sink and Enemy lose an score otherwise nothing happend and just a sign displayed that is '%' and it's because you remember you shot this location

        public bool Attack(List<char> enemyIsland, List<char> enemyIslandCover, int cubePosition)
        {
            if (enemyIsland[cubePosition] == '@')
            {
                int rowIndex = cubePosition - (cubePosition % 10);
                for (int i = rowIndex; i < rowIndex + 10; i++)
                {
                    if (enemyIsland[i] == '@')
                    {
                        enemyIsland[i] = '░';
                        enemyIslandCover[i] = '░';
                    }
                }
                gameEnigne.DrawIslands();
                EnemyShip--;

                return true;
            }
            else
            {
                enemyIslandCover[cubePosition] = '%';
                return false;
            }

        }

this is exactly happend when enemy attack but with few diffrent. look:

        public void EnemyAttack(List<char> MyIsland)
        {
            GameEngine.GetError("It's Turn of enemy | HIDE HIDE HIIIIIIIDE !!!! ");

            int index;
            Random rnd = new Random();
            if (indexOfAI > 0)
            {
                if (indexOfAI < 70)
                {
                    index = rnd.Next(indexOfAI, indexOfAI + 30);
                }
                else
                {
                    index = rnd.Next(indexOfAI, MyIsland.Count);
                }
            }
            else
            {
                index = rnd.Next(0, MyIsland.Count);
            }

            char saveChar = MyIsland[index];
            MyIsland[index] = 'Ö';
            gameEnigne.DrawIslands();
            GameEngine.GetError("Enemy Choose His Location!! DON'T MOVE!! Sheeeeesh");

            MyIsland[index] = saveChar;

            bool isThereAnyShip = false;

            if (MyIsland[index] == '@')
            {
                int Counter = index - (index % 10);

                for (int i = Counter; i < Counter + 10; i++)
                {
                    if (MyIsland[i] == '@')
                    {
                        MyIsland[i] = '░';
                        isThereAnyShip = true;
                    }
                }
            }

            gameEnigne.DrawIslands();
            if (isThereAnyShip)
            {
                YourShip--;
                GameEngine.GetError("Enemy Sink One Of Your Ship Admiral, What's The Next Order 
                ?");
                indexOfAI = 0;
            }
            else
            {
                GameEngine.GetSucces("The danger was eliminated. The enemy does not know the 
                location of our ship!!");
                if (indexOfAI == 0)
                {
                    SimpleEnemyAI(index, MyIsland);
                }
                else
                {
                    indexOfAI = 0;
                }
            }  
        }

enemy generate a random house of my island or sea and shot it if it's shot exactly on my ships then my score decreases but if he can't shot my ship then a method run named SimpleEnemyAI(). this method find near the shot and look at my ship. if there is ship in the area then set the enemy index closer.

SimpleEnemyAI Method:

         private void SimpleEnemyAI(int index , List<char> Island)
        {
            // it's a algorithm for Simple AI
            if (index > 10)
            {
                index = index - (index % 10) - 10;
            }
            bool isAnyShipFind = false;

            if (index < 70)
            {
                for (int i = index; i < index + 20; i++)
                {
                    if (Island[i] == '@')
                    {
                        indexOfAI = index;
                        isAnyShipFind = true;
                        break;
                    }
                }
            }
            else
            {
                for (int i = index; i < Island.Count; i++)
                {
                    if (Island[i] == '@')
                    {
                        indexOfAI = index;
                        isAnyShipFind = true;
                        break;
                    }
                }
            }

            if (isAnyShipFind)
            {
                GameEngine.GetError("But Careful! Enemy Have Radar And He Saw Vague Things In 
                It!!");
            }
            else
            {
                indexOfAI = 0;
            }
        }

And in order for the game not to be difficult, if you pay attention to the method, you will realize that this does not happen every time.

This was my exercise. I would be very grateful if you could please remind me of my problems and if there is anything that will improve me.

\$\endgroup\$
2
  • \$\begingroup\$ Why do you randomly switch the casing of your variable names? One minute your pascal casing (i.e. Counter) and the next you're camel casing (i.e. index, indexOfAI, etc). General standard is to have local variables and parameters be camelCased and have classes, member variables, etc be PascalCased. \$\endgroup\$
    – Shelby115
    Nov 4 '21 at 12:27
  • \$\begingroup\$ It would be nice if you can include a section with the entire code. I like to see what the program is doing before commenting and having to copy/paste all the sections together is tedious :-) \$\endgroup\$
    – jdt
    Nov 4 '21 at 12:29
2
\$\begingroup\$

Try to pay attention to how do you name things, because it's really important when other people read your code

Method names :

method names should always be a verb not a noun, because the method do things

  • myGameEngine.Engine() should be myGameEngine.startEngine() or myGameEngine.runEngine() or simply myGameEngine.start()

  • FirstTimeGenerate() should be GenerateFirstTime() or more clear name should be GenerateFirstFram()

  • Hint() should be displayHint() or PrintHint()

Properties names :

Field names should be a descriptive noun and doesn't use pronouns [your, my his, ...etc.]

  • _myIsland should be more descriptive like _userIsland

  • YourShip you use as a counter for user ships count, so you should name it UserShipsCount, similar to EnemyShip should be EnemyShipsCount

  • hint in GameEngine class should be hintCounter or be more descriptive displayedHintCounter


in the following peace

private bool FindBestPlace(int index, List<char> island)
{
    try
    {
        int Counter = index - (index % 10);  // << what is 10 
        for (int i = Counter; i < Counter + 10; i++)
        {
            if (island[i] != '■')
            {
                return false;
            }
        }
        if (index % 10 > 2 && index % 10 < 9)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    catch
    {
        return false;
    }
}

I don't know what is number 10 represent in this context, you should not use plain number in your code, instead you can define a constant and use it

const int ROW_LENTH = 10; 
\$\endgroup\$
2
  • \$\begingroup\$ Thanks for your explanation. int Counter = index - (index % 10); in this row of code i divide the remainder of the index by 10 and subtract it from the index. Think of it as 57. 57 divided by the remaining 10 is equal to 7. 7 minus 57 is 50. This was actually a way to get a rounded number so I could check a row completely (for example from 50 to 60) to see if there was a ship in that row. But I understand what you mean by using const. Thanks for the help \$\endgroup\$ Nov 4 '21 at 16:19
  • 1
    \$\begingroup\$ @CarlJohnson, reviewers will sometimes use a rhetorical question to guide your thinking, no explanation is really required =) \$\endgroup\$
    – jdt
    Nov 4 '21 at 19:13
1
\$\begingroup\$

Use meaningful names

I have to agree with Ibram, the way you name things is a bit confusing.

What is GetError supposed to do? A better name would have been ShowError.

The same goes for GetSucces. Why not rather ShowSuccessMessage.

Infinite loops

In HasAnyoneWon you have an infinite loop but break out when HowManyTime reaches 100. The following would have been more readable:

while (HowManyTime < 100)
{
    // ...
}

The same applies to GenerateShips:

public void GenerateShips(List<char> island)
{
    int counter = 0;
    var randomGenerator = new Random();
    while (counter < YourShip)
    {
        int index = randomGenerator.Next(0, island.Count);
        if (FindBestPlace(index, island))
        {
            island[index] = '@';
            island[index - 1] = '@';
            island[index + 1] = '@';
            counter++;
        }
    }
}

Splitting complexities into functions

You have way too much going on in void Engine(). I would suggest that you split it up into the following functions:

void Attach()
{
    // ...
}

void ShowHint()
{
    // ...
}

void ExitGame()
{
    // ...
}

public void Engine()
{
    if (!isGenerate)
    {
        FirstTimeGenerate();
    }
    DrawIslands();
    ShowInformation();
    string userInput = GetString("░ Choose a number > ");

    switch (userInput)
    {
        case "1":
            Attach();
            break;

        case "2":
            ShowHint();
            break;

        case "3":
            ExitGame();
            break;

        default:
            GetError("You Put Wrong Value!");
            break;
    }
}

Screen flickering

The game looks much more polished without the flickering that is caused by Console.Clear(). I would suggest that you rather use Console.SetCursorPosition and overwrite the necessary text.

\$\endgroup\$
1
  • \$\begingroup\$ thank you for the help. \$\endgroup\$ Nov 4 '21 at 16:24

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.