Can’t place a ship part next to another part

  Kiến thức lập trình

I started working on a project in C# using Windows Forms, making a sea battle game.

How my game works:

When the program starts, a dialog is displayed where we have two options:
-Player vs player
-Player vs computer

After that dialog, another dialog appears where we choose the following:

4×4
8×8
10×10
(what dimensions do we want the fields to be when we place the ships)

Then, after that selection, a dialog appears with the display of those fields and it looks like this:

For example I chose 4×4:

4×4 field

I will give an example and what is the output:

I choose a 4×4 field for placing ships (one size 1 ship and two size 2 ships are placed on a 4×4 field)

First I was offered a size 1 ship.

I set it at the following coordinates i=0, j=0 (i is a column, j is a row) Size 1 ship placed

Then after that it’s the turn of the size 2 ship, and now I need to place that ship.

I place the first part of the ship at i = 2, j =0, and place it successfully. First part of size 2 ship placed

Then I try to place another part of that ship (size 2) at i = 2, j =1, unsuccessfully it says “you cannot place a part of the ship at this position try again” unsuccessful

I also try to set that other part of the ship to i = 3, j = 0, but it still fails. unsuccessful

I’ve been trying to solve the problem for an hour now, but I don’t know where the error/bug is and why it’s not working.

Here is the code of that class:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace oop_template
{
    public partial class ShipPlacementForm : Form
    {
        private int tableSize;
        private List<Ship> ships;
        private List<Ship> placedShips;
        private Button[,] tableButtons;
        private int currentShipIndex;
        private Ship currentShip;
        private bool horizontalPlacement;

        public List<Ship> PlacedShips
        {
            get { return placedShips; }
        }

        public ShipPlacementForm(int tableSize, List<Ship> ships)
        {
            this.tableSize = tableSize;
            this.ships = ships;
            this.placedShips = new List<Ship>();
            CustomInitializeComponent();

            // Initialize placement of the first ship
            this.currentShipIndex = 0;
            SetCurrentShip();
            deleteButton.Enabled = false; // Disable delete until at least one ship is placed
        }

        private void CustomInitializeComponent()
        {
            this.SuspendLayout();

            // Dynamically create buttons for the table fields
            tableButtons = new Button[tableSize, tableSize];
            for (int i = 0; i < tableSize; i++)
            {
                for (int j = 0; j < tableSize; j++)
                {
                    tableButtons[i, j] = new Button();
                    tableButtons[i, j].Size = new Size(30, 30);
                    tableButtons[i, j].Location = new Point(30 * i, 30 * j);
                    tableButtons[i, j].Click += new EventHandler(TableButton_Click);
                    this.Controls.Add(tableButtons[i, j]);
                }
            }

            // Label for displaying the current ship
            this.currentShipLabel = new Label();
            this.currentShipLabel.Location = new Point(30 * tableSize, 30);
            this.currentShipLabel.AutoSize = true;
            this.Controls.Add(this.currentShipLabel);

            // Button for confirmation
            this.confirmButton = new Button();
            this.confirmButton.Text = "Confirm";
            this.confirmButton.Location = new Point(30 * tableSize, 60);
            this.confirmButton.Click += new EventHandler(ConfirmButton_Click);
            this.confirmButton.Enabled = false; // Disabled until all ships are placed
            this.Controls.Add(this.confirmButton);

            // Button for deleting a ship
            this.deleteButton = new Button();
            this.deleteButton.Text = "Delete Ship";
            this.deleteButton.Location = new Point(30 * tableSize, 90);
            this.deleteButton.Click += new EventHandler(DeleteButton_Click);
            this.deleteButton.Enabled = false; // Disabled until at least one ship is placed
            this.Controls.Add(this.deleteButton);

            // Set form size
            this.ClientSize = new System.Drawing.Size(30 * (tableSize + 2), 30 * tableSize);
            this.Name = "ShipPlacementForm";
            this.Text = "Ship Placement";
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        private void TableButton_Click(object sender, EventArgs e)
        {
            Button clickedButton = sender as Button;
            if (clickedButton == null)
                return;

            int x = clickedButton.Location.X / 30;
            int y = clickedButton.Location.Y / 30;

            if (CanPlaceShipPart(x, y))
            {
                PlaceShipPart(x, y);
                currentShip.Positions.Add(new Point(x, y));

                if (currentShip.Positions.Count == 1)
                {
                    // First part of the ship is placed, wait for the second part to determine orientation
                    deleteButton.Enabled = false; // Disable deletion until the entire ship is placed
                }
                else if (currentShip.Positions.Count == 2)
                {
                    // Determine the orientation of the ship based on the first two positions
                    horizontalPlacement = currentShip.Positions[0].Y == currentShip.Positions[1].Y;
                }

                // Check if the ship is complete
                if (currentShip.Positions.Count == currentShip.Size)
                {
                    placedShips.Add(currentShip);
                    currentShipIndex++;

                    if (currentShipIndex < ships.Count)
                    {
                        SetCurrentShip();
                    }
                    else
                    {
                        currentShipLabel.Text = "All ships are placed.";
                        confirmButton.Enabled = true;
                        deleteButton.Enabled = true;
                    }
                }
            }
            else
            {
                MessageBox.Show("You cannot place a ship part on this position. Please try again.");
            }
        }

        private void DeleteButton_Click(object sender, EventArgs e)
        {
            currentShipLabel.Text = "Click on any part of the ship you want to delete.";
            foreach (var ship in placedShips)
            {
                foreach (var position in ship.Positions)
                {
                    tableButtons[position.X, position.Y].Click += DeleteShipClick;
                }
            }
        }

        private void DeleteShipClick(object sender, EventArgs e)
        {
            Button clickedButton = sender as Button;
            if (clickedButton == null)
                return;

            int x = clickedButton.Location.X / 30;
            int y = clickedButton.Location.Y / 30;

            Ship shipToDelete = null;

            foreach (var ship in placedShips)
            {
                if (ship.Positions.Contains(new Point(x, y)))
                {
                    shipToDelete = ship;
                    break;
                }
            }

            if (shipToDelete != null)
            {
                DeleteShip(shipToDelete);
                currentShipLabel.Text = $"Place a ship of size {shipToDelete.Size}";
                currentShip = shipToDelete;
                currentShipIndex = ships.IndexOf(shipToDelete);

                foreach (var position in shipToDelete.Positions)
                {
                    tableButtons[position.X, position.Y].Click -= DeleteShipClick;
                }
            }

            deleteButton.Enabled = placedShips.Count > 0;
        }

        private void DeleteShip(Ship ship)
        {
            foreach (var position in ship.Positions)
            {
                tableButtons[position.X, position.Y].BackColor = default(Color);
                tableButtons[position.X, position.Y].Enabled = true;
            }
            placedShips.Remove(ship);
            ships.Insert(currentShipIndex, ship); // Return the ship to the list for re-placement
        }

        private void ConfirmButton_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.OK;
            this.Close();
        }

        private void SetCurrentShip()
        {
            currentShip = ships[currentShipIndex];
            currentShip.Positions.Clear();
            currentShipLabel.Text = $"Place a ship of size {currentShip.Size}";
            deleteButton.Enabled = placedShips.Count > 0;
        }

        private bool CanPlaceShipPart(int x, int y)
        {
            if (tableButtons[x, y].BackColor == Color.Gray)
                return false; // The field is already taken

            // Check continuity of ship placement
            if (currentShip.Positions.Count > 0)
            {
                var lastPos = currentShip.Positions[currentShip.Positions.Count - 1];

                // Check if the second part of the ship can be placed next to the first one
                bool validHorizontalPos = (x == lastPos.X && Math.Abs(y - lastPos.Y) == 1);
                bool validVerticalPos = (y == lastPos.Y && Math.Abs(x - lastPos.X) == 1);

                if (validHorizontalPos || validVerticalPos)
                {
                    // Check if the adjacent field is taken
                    foreach (var (dx, dy) in new[] { (-1, 0), (1, 0), (0, -1), (0, 1) })
                    {
                        int nx = x + dx;
                        int ny = y + dy;

                        if (nx >= 0 && nx < tableSize && ny >= 0 && ny < tableSize)
                        {
                            if (tableButtons[nx, ny].BackColor == Color.Gray)
                                return false; // The adjacent field is taken
                        }
                    }

                    return true;
                }
                else
                {
                    return false;
                }
            }

            return true;
        }

        private void PlaceShipPart(int x, int y)
        {
            tableButtons[x, y].BackColor = Color.Gray; // Color the field gray
            tableButtons[x, y].Enabled = false; // Disable clicking on the same field again
        }

        private Label currentShipLabel;
        private Button confirmButton;
        private Button deleteButton;
    }
}

Thank you for your time.

PS: sorry if I am grammatically incorrect somewhere, English is not my native language

New contributor

Огњен Стојановић is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

LEAVE A COMMENT