How to check if the area is filled or not?

so, I am basically making a puzzle game where a big shape(in black) is given and the player needs to fill that shape/shadow with different smaller sized shapes.
For 1st level, I made it so only a specific shape can be placed at the positions, but now for level 2, I need to make it so that any shape can be placed anywhere and it’s correct as long as the whole black shape is covered.

Level 1:


So as you can see I am showing a reference image on the left side which the player needs to copy.

for Level 2:


For Level 2 I need something like this where the user can put any shape anywhere on the black, and the condition for correct is that it needs to cover the whole area and should not go out of the black.

Currently, I have this code for my drag and drop script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class Tess1DragDrop : MonoBehaviour, IPointerDownHandler, IBeginDragHandler, IEndDragHandler, IDragHandler {

    [SerializeField] private Canvas canvas;

    private RectTransform rectTransform;
    public Vector3 startPos;
    private CanvasGroup canvasGroup;
    private GameObject onShadow;
    private GameObject onPanel;
    Camera cam;
    private Vector3 CurrentPos;
    public bool atPlace;

    private void Awake() {
        onShadow = GameObject.FindGameObjectWithTag("onShadow");
        onPanel = GameObject.FindGameObjectWithTag("onPanel");
        rectTransform = GetComponent<RectTransform>();
        canvasGroup = GetComponent<CanvasGroup>();
        startPos= rectTransform.anchoredPosition;
        cam = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
    }

    public void Update(){
    }

    public void OnBeginDrag(PointerEventData eventData) {
    //  Debug.Log("OnBeginDrag");
        eventData.pointerDrag.transform.SetParent(onShadow.transform);
        canvasGroup.alpha = .6f;
        canvasGroup.blocksRaycasts = false;
        this.tag = "Untagged";
    }

    public void OnDrag(PointerEventData eventData) {
        //Debug.Log("OnDrag");
        rectTransform.anchoredPosition += eventData.delta / canvas.scaleFactor;
    }

    public void OnEndDrag(PointerEventData eventData) {
    //    Debug.Log("OnEndDrag");
        if(eventData.pointerDrag.tag!="Panel"){
                CurrentPos = cam.WorldToViewportPoint(transform.position);
        
                canvasGroup.alpha = 1f;
                canvasGroup.blocksRaycasts = true;
        
                if(rectTransform.anchoredPosition.y < -125f){
                eventData.pointerDrag.transform.SetParent(onPanel.transform);
                rectTransform.anchoredPosition = startPos;
                }
        
                if(rectTransform.anchoredPosition.y > -125f){
                //eventData.pointerDrag.transform.SetParent(onShadow.transform);
                StartCoroutine(moveBack());
                }
        
                if(CurrentPos.x > 0.9f){
                    eventData.pointerDrag.transform.SetParent(onPanel.transform);
                    rectTransform.anchoredPosition = startPos;
                }        
                else if(CurrentPos.x < 0.1f){
                    eventData.pointerDrag.transform.SetParent(onPanel.transform);
                    rectTransform.anchoredPosition = startPos;
                }
        
                else if(CurrentPos.y > 1f){
                    eventData.pointerDrag.transform.SetParent(onPanel.transform);
                    rectTransform.anchoredPosition = startPos;
                }}
    }

    IEnumerator moveBack(){
        yield return new WaitForSeconds(0.01f);
        if(gameObject.tag != "atPlace"){
            gameObject.transform.SetParent(onPanel.transform);
            gameObject.GetComponent<RectTransform>().anchoredPosition = startPos;
            gameObject.tag = "Untagged";
            gameObject.GetComponent<Image>().raycastTarget = true;
        }

    }

    public void OnPointerDown(PointerEventData eventData) {  //imp method
     //   Debug.Log("OnPointerDown");
    }

}

and for the item slot script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class Tess1ItemSlot : MonoBehaviour, IDropHandler {
	private GameObject onShadow;
	private GameObject onPanel;
    //public bool atPlace;


	public void Start(){
		onShadow = GameObject.FindGameObjectWithTag("onShadow");
		onPanel = GameObject.FindGameObjectWithTag("onPanel");
      //  atPlace = false;
	}

    public void OnDrop(PointerEventData eventData) {
      //  Debug.Log("OnDrop");
      if(eventData.pointerDrag.tag!="Panel"){
        if (eventData.pointerDrag != null && eventData.pointerDrag.GetComponent<Image>().sprite == gameObject.GetComponent<Image>().sprite) {
        	eventData.pointerDrag.transform.SetParent(onShadow.transform);
            eventData.pointerDrag.GetComponent<RectTransform>().anchoredPosition = GetComponent<RectTransform>().anchoredPosition;
            eventData.pointerDrag.tag = "atPlace";
            eventData.pointerDrag.GetComponent<Image>().raycastTarget = false;
        //    atPlace = true;
            
        }
        if(eventData.pointerDrag.GetComponent<Image>().sprite != gameObject.GetComponent<Image>().sprite){
       // 	Debug.Log("NotSame");
        	eventData.pointerDrag.transform.SetParent(onPanel.transform);
        	eventData.pointerDrag.GetComponent<RectTransform>().anchoredPosition = eventData.pointerDrag.GetComponent<Tess1DragDrop>().startPos;
          eventData.pointerDrag.tag = "Untagged";
          eventData.pointerDrag.GetComponent<Image>().raycastTarget = true;
       //     atPlace = false;
        }
    }
  }

}

Tough problem! I don’t see it in your code, but it looks like you are coding yourself into a corner and would think you want to generalize the problem.

If you don’t do this, you’re going to have to hand code every game level you create. If you do this, you can easily create an infinite number of different levels with much less effort

I’ve done quite a bit of hex-grid game coding, so all of the following is doable and not that difficult.

It looks like your game area can be described in a triangular grid system - because it looks like all the shapes can be broken down in to isosceles triangles. So you will need to create a grid system. The centers of each triangle in the grid will be the drop locations.

Then you need an array/list of data structures or class objects that hold some information about each triangle in the grid. I’m talking off the top of my head here, so this is spitballing you a solution (seems like your working in 2D) so in my pseudo code:

class GridLocation // will need to create a constructor with these memebers:
     Vector2 center_positon // x y pos to reference back to the grid
     bool isfilled = false
     GameObject filledObject;
      // could also include a variable for games such as the sh
      // where specific positions only accept a correct shape

// when you create a new game, you'll need to initialize the screen grid shape, and then the array and GridLocation objects

 // next link the array to the grid using the GridLocation class
for each screen gird row and column in a loop
     array[ i, j ] = new GridLocation( gridlocation_x_y, false, null );

One inescapable issue here is you have to be be able to map the actual grid x and y coordinates to array indices. That way when the user drags a shape on to the game area, you can snap it to the grid screen location and use the snapped x y coordinates to update the correct element in array. This is not that difficult to do, but a triangular grid-to-array mapping is not going to be as simple as if the grid were made of square elements.

When the player drags a shape to the screen and drops it on the grid, use mouse x and y to snap to a grid center, and the map the respective array column and row values and then update the array. You’ll also check to see if isFilled is already true, then you may not allow the drop.

   array[ i, j ].isFilled = true
   array[ i, j ].filledObject = the game object dropped

Now it is a simple matter to loop through the array to see if any of the isFilled are true, and if all are true you know your grid is filled!

Another critical issue is if you have drag and drop shapes that composed of two triangles, like your rhombus in the second image you posted. There you’ll have to come up with a scheme of filling multiple grid triangles with one shape, and marking isFilled and the filledObject fields of the respective array elements appropriately. You’d also need information about each shape, so probably a “Shape” class that contains how many grid positions it can fill; a link to the shape gameobject; and so on. That is tougher to do but it is not an impossible problem.

Better to rethink and refactor - fix your code now before you get too far down a road that is not scalable!