нужно будет рассказать про этот код прямо чтобы было понятно как он работает чтобы каждый метод был описан как он работает особое внимание для тех методов где идет описание для непосредственно самого процесса игры
package com.example.memorymaster;

import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.Collections;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import android.content.Intent;

import nl.dionsegijn.konfetti.KonfettiView;
import nl.dionsegijn.konfetti.models.Shape;
import nl.dionsegijn.konfetti.models.Size;

public class PairMatchingGame extends AppCompatActivity {

    private GridLayout gridLayout;
    private ArrayList<Button> cardButtons;
    private MemoryCard[] cardValues;
    private Button firstSelectedCard = null;
    private boolean isFlipping = false;
    private int matchedPairs = 0;
    private int currentLevel = 1;
    private int totalMoves = 0;
    private int moves = 0;
    private long totalTime = 0;
    private TextView movesTextView;
    private long startTime;
    private Handler timerHandler = new Handler();
    private TextView timerTextView;
    private String nickname;
    private SoundManager soundManager;
    private KonfettiView konfettiView;
    private String playerUserId;
    private FirebaseHelper firebaseHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pair_matching_game);
        konfettiView = findViewById(R.id.konfettiView);
        soundManager = SoundManager.getInstance(this);

        gridLayout = findViewById(R.id.gridLayout);
        cardButtons = new ArrayList<>();
        movesTextView = findViewById(R.id.movesTextView);
        timerTextView = findViewById(R.id.timerTextView);

        SharedPreferences preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
        nickname = preferences.getString("nickname", "");

        firebaseHelper = new FirebaseHelper();

        FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
        if (currentUser != null) {
            playerUserId = currentUser.getUid();
        } else {
            Intent intent = new Intent(PairMatchingGame.this, LoginActivity.class);
            startActivity(intent);
            finish();
            return;
        }

        setupGameForLevel(currentLevel);
        startTimer();
    }

    private void setupGameForLevel(int level) {
        int numberOfCards;
        switch (level) {
            case 1:
                numberOfCards = 6;  // Easy level (6 cards)
                break;
            case 2:
                numberOfCards = 12; // Medium level (12 cards)
                break;
            default:
                numberOfCards = 16; // Hard level (16 cards)
        }

        cardValues = new MemoryCard[numberOfCards];
        for (int i = 0; i < numberOfCards / 2; i++) {
            cardValues[i] = new MemoryCard(i + 1);
            cardValues[i + numberOfCards / 2] = new MemoryCard(i + 1);
        }

        setupGameGrid(numberOfCards);
        updateMovesDisplay();

        // Reset matchedPairs and moves for the new level
        matchedPairs = 0;
        moves = 0;
    }

    private void setupGameGrid(int numberOfCards) {
        gridLayout.removeAllViews();
        cardButtons.clear();

        int columnCount = (int) Math.sqrt(numberOfCards);
        gridLayout.setColumnCount(columnCount);

        ArrayList<MemoryCard> cardList = new ArrayList<>();
        Collections.addAll(cardList, cardValues);
        Collections.shuffle(cardList);

        for (MemoryCard card : cardList) {
            Button button = new Button(this);
            button.setTag(card);
            button.setText("");
            button.setBackgroundResource(R.drawable.card_back);

            GridLayout.LayoutParams params = new GridLayout.LayoutParams();
            params.width = 0;
            params.height = GridLayout.LayoutParams.WRAP_CONTENT;
            params.columnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f);
            params.rowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f);
            button.setLayoutParams(params);

            button.setOnClickListener(this::onCardClick);
            cardButtons.add(button);
            gridLayout.addView(button);
        }
    }

    private void onCardClick(View view) {
        if (isFlipping) return;

        Button selectedButton = (Button) view;
        MemoryCard selectedCard = (MemoryCard) selectedButton.getTag();

        soundManager.playButtonClickSound();

        if (!selectedButton.getText().toString().isEmpty()) {
            return; // Already matched
        }

        selectedButton.setText(String.valueOf(selectedCard.getValue()));
        selectedButton.setBackgroundResource(R.drawable.card_front);

        if (firstSelectedCard == null) {
            firstSelectedCard = selectedButton;
        } else {
            moves++;
            totalMoves++;
            updateMovesDisplay();

            MemoryCard firstCard = (MemoryCard) firstSelectedCard.getTag();
            if (firstCard.getValue() == selectedCard.getValue()) {
                soundManager.playCorrectSound();
                firstSelectedCard.setEnabled(false);
                selectedButton.setEnabled(false);
                matchedPairs++;

                if (matchedPairs == cardValues.length / 2) {
                    onGameWon();
                }
                firstSelectedCard = null;
            } else {
                soundManager.playWrongSound();
                isFlipping = true;
                new Handler().postDelayed(() -> {
                    firstSelectedCard.setText("");
                    selectedButton.setText("");
                    firstSelectedCard.setBackgroundResource(R.drawable.card_back);
                    selectedButton.setBackgroundResource(R.drawable.card_back);
                    firstSelectedCard = null;
                    isFlipping = false;
                }, 1000);
            }
        }
    }

    private void updateMovesDisplay() {
        movesTextView.setText("Ходы: " + moves + " (Всего: " + totalMoves + ")");
    }

    private void startTimer() {
        startTime = System.currentTimeMillis() - totalTime * 1000;
        timerHandler.postDelayed(timerRunnable, 0);
    }

    private Runnable timerRunnable = new Runnable() {
        @Override
        public void run() {
            long millis = System.currentTimeMillis() - startTime;
            totalTime = millis / 1000;
            int seconds = (int) (totalTime % 60);
            int minutes = (int) (totalTime / 60);

            timerTextView.setText(String.format("%d:%02d", minutes, seconds));
            timerHandler.postDelayed(this, 500);
        }
    };

    private void onGameWon() {
        timerHandler.removeCallbacks(timerRunnable);

        if (currentLevel >= 3) {
            gameCompleted();
            return;
        }

        currentLevel++;
        soundManager.playLevelUpSound();
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Уровень " + (currentLevel - 1) + " пройден!")
                .setMessage("Готовы к Уровню " + currentLevel + "?\nХоды: " + moves + "\nВремя: " + formatTime(totalTime))
                .setPositiveButton("Продолжить", (dialog, which) -> {
                    soundManager.playButtonClickSound();
                    setupGameForLevel(currentLevel);
                    startTimer();
                })
                .setCancelable(false)
                .show();
    }

    private String formatTime(long totalSeconds) {
        long minutes = totalSeconds / 60;
        long seconds = totalSeconds % 60;
        return String.format("%d:%02d", minutes, seconds);
    }

    private void gameCompleted() {
        firebaseHelper.updatePairMatchingStats(playerUserId, totalMoves, totalTime, new FirebaseHelper.UpdateStatsCallback() {
            @Override
            public void onUpdateCompleted(boolean newRecord) {
                if (newRecord) {
                    soundManager.playNewRecordSound();
                    showConfetti();
                } else {
                    soundManager.playVictorySound();
                }

                AlertDialog.Builder builder = new AlertDialog.Builder(PairMatchingGame.this);
                builder.setTitle("Поздравляем!")
                        .setMessage("Вы прошли все уровни!\nВсего ходов: " + totalMoves + "\nВремя: " + formatTime(totalTime))
                        .setPositiveButton("Играть снова", (dialog, which) -> {
                            soundManager.playButtonClickSound();
                            resetGame();
                        })
                        .setNegativeButton("Выйти", (dialog, which) -> {
                            soundManager.playButtonClickSound();
                            finish();
                        })
                        .show();
            }

            @Override
            public void onUpdateFailed(Exception e) {
                Toast.makeText(PairMatchingGame.this, "Ошибка обновления статистики: " + e.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
    }

    private void resetGame() {
        currentLevel = 1;
        totalMoves = 0;
        totalTime = 0;
        matchedPairs = 0;
        moves = 0;
        setupGameForLevel(currentLevel);
        startTimer();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        timerHandler.removeCallbacks(timerRunnable);
        soundManager.release();
    }

    private void showConfetti() {
        konfettiView.build()
                .addColors(Color.YELLOW, Color.GREEN, Color.MAGENTA)
                .setDirection(0.0, 359.0)
                .setSpeed(1f, 5f)
                .setFadeOutEnabled(true)
                .setTimeToLive(2000L)
                .addShapes(Shape.Square.INSTANCE, Shape.Circle.INSTANCE)
                .addSizes(new Size(12, 5f))
                .setPosition(-50f, konfettiView.getWidth() + 50f, -50f, -50f)
                .streamFor(300, 5000L);
    }
}