private bool TryPlace(Cell[,,] map, Dictionary <int, Vector3Int> available, Dictionary <int, Vector3Int> all, Vector3Int leadIndex)
 {
     if (OccupiedPieces(leadIndex, pieceSize) == 0 && OccupiedPieces(leadIndex - new Vector3Int(0, 0, 1), pieceSize) == pieceSize * pieceSize)
     {
         MahjongPiece newPiece = Instantiate(piecePrefab);
         newPiece.Place(GridToWorldPoint(leadIndex));
         all.Add(newPiece.GetId(), leadIndex);
         SetCellState(map, available, leadIndex, newPiece);
         return(true);
     }
     return(false);
 }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.S))
        {
            SaveMap();
        }
        if (Input.GetKeyDown(KeyCode.L))
        {
            LoadMap();
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            map.InitPieces(8);
        }
        if (Input.GetKeyDown(KeyCode.C))
        {
            map.UnloadMap();
        }

        if (Input.GetMouseButtonDown(0))
        {
            Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(cameraRay, out RaycastHit hit))
            {
                if (map.TryPlace(map.WorldPointToGrid(hit.point, true)))
                {
                    print("Valid hit " + hit.point);
                }
                else
                {
                    print("Invalid hit " + hit.point);
                }
            }
        }
        if (Input.GetMouseButtonDown(1))
        {
            Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(cameraRay, out RaycastHit hit))
            {
                MahjongPiece piece = hit.collider.GetComponentInParent <MahjongPiece>();
                if (piece && map.TryRemove(map.PieceToIndex(piece), false))
                {
                    print("Valid hit " + hit.point);
                }
                else
                {
                    print("Invalid hit " + hit.point);
                }
            }
        }
    }
    private bool TryRemove(Cell[,,] map, Dictionary <int, Vector3Int> available, Dictionary <int, Vector3Int> all, Vector3Int index, bool sideBlocking)
    {
        if (!map[index.x, index.y, index.z].piece)
        {
            return(false);
        }

        if (!IsPieceAvailable(index, sideBlocking))
        {
            return(false);
        }

        MahjongPiece piece = map[index.x, index.y, index.z].piece;

        SetCellState(map, available, map[index.x, index.y, index.z].leadIndex, null);
        all.Remove(map[index.x, index.y, index.z].piece.GetId());
        piece.Remove();
        return(true);
    }
 public Vector3Int PieceToIndex(MahjongPiece piece)
 {
     pieceLeadIndexes.TryGetValue(piece.GetId(), out Vector3Int index);
     return(index);
 }
    private void SetCellState(Cell[,,] map, Dictionary <int, Vector3Int> available, Vector3Int leadIndex, MahjongPiece piece)
    {
        Dictionary <int, Vector3Int> afflictedPieces = new Dictionary <int, Vector3Int>();

        if (piece)
        {
            afflictedPieces.Add(piece.GetId(), leadIndex);
        }
        else
        {
            afflictedPieces.Add(map[leadIndex.x, leadIndex.y, leadIndex.z].piece.GetId(), leadIndex);
        }

        for (int i = 0; i < pieceSize; i++)
        {
            for (int p = 0; p < pieceSize; p++)
            {
                map[leadIndex.x + i, leadIndex.y + p, leadIndex.z].piece = piece;
                if (piece)
                {
                    map[leadIndex.x + i, leadIndex.y + p, leadIndex.z].leadIndex = leadIndex;
                    if (leadIndex.z > 0)
                    {
                        map[leadIndex.x + i, leadIndex.y + p, leadIndex.z - 1].isTopBlocked = true;
                        if (!afflictedPieces.ContainsKey(map[leadIndex.x + i, leadIndex.y + p, leadIndex.z - 1].piece.GetId()))
                        {
                            afflictedPieces.Add(map[leadIndex.x + i, leadIndex.y + p, leadIndex.z - 1].piece.GetId(), new Vector3Int(leadIndex.x + i, leadIndex.y + p, leadIndex.z - 1));
                        }
                    }

                    if (i == 0)
                    {
                        if (!IsOutOfMap(new Vector3Int(leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z)) && map[leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z].piece)
                        {
                            map[leadIndex.x + i, leadIndex.y + p, leadIndex.z].isLeftBlocked      = true;
                            map[leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z].isRightBlocked = true;
                            if (!afflictedPieces.ContainsKey(map[leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z].piece.GetId()))
                            {
                                afflictedPieces.Add(map[leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z].piece.GetId(), new Vector3Int(leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z));
                            }
                        }
                    }
                    else if (i == pieceSize - 1)
                    {
                        if (!IsOutOfMap(new Vector3Int(leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z)) && map[leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z].piece)
                        {
                            map[leadIndex.x + i, leadIndex.y + p, leadIndex.z].isRightBlocked    = true;
                            map[leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z].isLeftBlocked = true;
                            if (!afflictedPieces.ContainsKey(map[leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z].piece.GetId()))
                            {
                                afflictedPieces.Add(map[leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z].piece.GetId(), new Vector3Int(leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z));
                            }
                        }
                    }
                }
                else
                {
                    map[leadIndex.x + i, leadIndex.y + p, leadIndex.z].leadIndex = default;
                    if (leadIndex.z > 0)
                    {
                        map[leadIndex.x + i, leadIndex.y + p, leadIndex.z - 1].isTopBlocked = false;
                        if (!afflictedPieces.ContainsKey(map[leadIndex.x + i, leadIndex.y + p, leadIndex.z - 1].piece.GetId()))
                        {
                            afflictedPieces.Add(map[leadIndex.x + i, leadIndex.y + p, leadIndex.z - 1].piece.GetId(), new Vector3Int(leadIndex.x + i, leadIndex.y + p, leadIndex.z - 1));
                        }
                    }

                    if (i == 0)
                    {
                        if (!IsOutOfMap(new Vector3Int(leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z)) && map[leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z].piece)
                        {
                            map[leadIndex.x + i, leadIndex.y + p, leadIndex.z].isLeftBlocked      = false;
                            map[leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z].isRightBlocked = false;
                            if (!afflictedPieces.ContainsKey(map[leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z].piece.GetId()))
                            {
                                afflictedPieces.Add(map[leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z].piece.GetId(), new Vector3Int(leadIndex.x + i - 1, leadIndex.y + p, leadIndex.z));
                            }
                        }
                    }
                    else if (i == pieceSize - 1)
                    {
                        if (!IsOutOfMap(new Vector3Int(leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z)) && map[leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z].piece)
                        {
                            map[leadIndex.x + i, leadIndex.y + p, leadIndex.z].isRightBlocked    = false;
                            map[leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z].isLeftBlocked = false;
                            if (!afflictedPieces.ContainsKey(map[leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z].piece.GetId()))
                            {
                                afflictedPieces.Add(map[leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z].piece.GetId(), new Vector3Int(leadIndex.x + i + 1, leadIndex.y + p, leadIndex.z));
                            }
                        }
                    }
                }
            }
        }

        CalcAvailablePieces(map, available, afflictedPieces);
    }