<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MemoryMaster"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MemoryGame" />
        <activity android:name=".ProfileActivity" />
        <activity android:name=".DifficultySelectionActivity" />
        <activity android:name=".ReactionGame" />
        <activity android:name=".TileSelectionActivity" />
        <activity android:name=".PairMatchingGame" />
        <activity android:name=".ReactionGameModeSelection" />
        <activity android:name=".LoginActivity"></activity>
        <activity android:name=".RegisterActivity"></activity>
    </application>

</manifest>





package com.example.memorymaster;

import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;

import androidx.appcompat.app.AppCompatActivity;

public class DifficultySelectionActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_difficulty_selection);

        Button btnEasy = findViewById(R.id.btn_easy);
        Button btnMedium = findViewById(R.id.btn_medium);
        Button btnHard = findViewById(R.id.btn_hard);

        btnEasy.setOnClickListener(v -> startGameWithDifficulty(4)); // Легкая сложность - 4 плитки
        btnMedium.setOnClickListener(v -> startGameWithDifficulty(9)); // Средняя сложность - 9 плиток
        btnHard.setOnClickListener(v -> startGameWithDifficulty(12)); // Сложная - 12 плиток
    }

    private void startGameWithDifficulty(int tileCount) {
        Intent intent = new Intent(DifficultySelectionActivity.this, MemoryGame.class);
        intent.putExtra("tileCount", tileCount); // Передаем выбранную сложность
        startActivity(intent);
    }
}









package com.example.memorymaster;

import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

public class FirebaseHelper {
    private DatabaseReference databaseReference;

    public FirebaseHelper() {
        FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
        databaseReference = firebaseDatabase.getReference();
    }

    // Callback interface
    public interface UpdateStatsCallback {
        void onUpdateCompleted(boolean newRecord);
        void onUpdateFailed(Exception e);
    }

    // -------- Memory Game Stats --------
    public void updateMemoryGameStats(String nickname, String difficulty, int newScore, long newTime, UpdateStatsCallback callback) {
        String path = "memory_game_statistics/" + nickname + "/" + difficulty;
        DatabaseReference userStatsRef = databaseReference.child(path);

        userStatsRef.get().addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                MemoryGameStats stats = task.getResult().getValue(MemoryGameStats.class);
                final boolean[] newRecord = {false};  // Используем массив для обхода ограничения
                if (stats == null) {
                    // User doesn't have stats yet
                    stats = new MemoryGameStats();
                    stats.setBestScore(newScore);
                    stats.setBestScoreTime(newTime);
                    stats.setBestTime(newTime);
                    stats.setBestTimeScore(newScore);
                    newRecord[0] = true;
                } else {
                    if (newTime < stats.getBestTime()) {
                        stats.setBestTime(newTime);
                        stats.setBestTimeScore(newScore);
                        newRecord[0] = true;
                    }
                    if (newScore > stats.getBestScore()) {
                        stats.setBestScore(newScore);
                        stats.setBestScoreTime(newTime);
                        newRecord[0] = true;
                    }
                }
                final MemoryGameStats finalStats = stats;
                userStatsRef.setValue(finalStats).addOnCompleteListener(task1 -> {
                    if (task1.isSuccessful()) {
                        if (callback != null) {
                            callback.onUpdateCompleted(newRecord[0]);
                        }
                    } else {
                        if (callback != null) {
                            callback.onUpdateFailed(task1.getException());
                        }
                    }
                });
            } else {
                if (callback != null) {
                    callback.onUpdateFailed(task.getException());
                }
            }
        });
    }

    // -------- Reaction Game Stats --------
    public void updateReactionGameStats(String nickname, int reactionTime, UpdateStatsCallback callback) {
        String path = "reaction_game_statistics/" + nickname;
        DatabaseReference userStatsRef = databaseReference.child(path);

        userStatsRef.get().addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                Integer bestReactionTime = task.getResult().getValue(Integer.class);
                boolean newRecord = false;
                if (bestReactionTime == null || reactionTime < bestReactionTime) {
                    userStatsRef.setValue(reactionTime).addOnCompleteListener(task1 -> {
                        if (task1.isSuccessful()) {
                            if (callback != null) {
                                callback.onUpdateCompleted(true);
                            }
                        } else {
                            if (callback != null) {
                                callback.onUpdateFailed(task1.getException());
                            }
                        }
                    });
                } else {
                    if (callback != null) {
                        callback.onUpdateCompleted(false);
                    }
                }
            } else {
                if (callback != null) {
                    callback.onUpdateFailed(task.getException());
                }
            }
        });
    }

    // -------- Pair Matching Game Stats --------
    public void updatePairMatchingStats(String nickname, int moves, long time, UpdateStatsCallback callback) {
        String path = "pair_matching_statistics/" + nickname;
        DatabaseReference userStatsRef = databaseReference.child(path);

        userStatsRef.get().addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                PairMatchingStats stats = task.getResult().getValue(PairMatchingStats.class);
                final boolean[] newRecord = {false};  // Используем массив для обхода ограничения
                if (stats == null) {
                    // User doesn't have stats yet
                    stats = new PairMatchingStats();
                    stats.setBestMoves(moves);
                    stats.setBestTime(time);
                    stats.setFastestMoves(moves);
                    stats.setFastestTime(time);
                    newRecord[0] = true;
                } else {
                    if (moves < stats.getBestMoves()) {
                        stats.setBestMoves(moves);
                        stats.setBestTime(time);
                        newRecord[0] = true;
                    }
                    if (time < stats.getFastestTime()) {
                        stats.setFastestMoves(moves);
                        stats.setFastestTime(time);
                        newRecord[0] = true;
                    }
                }
                final PairMatchingStats finalStats = stats;
                userStatsRef.setValue(finalStats).addOnCompleteListener(task1 -> {
                    if (task1.isSuccessful()) {
                        if (callback != null) {
                            callback.onUpdateCompleted(newRecord[0]);
                        }
                    } else {
                        if (callback != null) {
                            callback.onUpdateFailed(task1.getException());
                        }
                    }
                });
            } else {
                if (callback != null) {
                    callback.onUpdateFailed(task.getException());
                }
            }
        });
    }

    // MemoryGameStats model class
    public static class MemoryGameStats {
        private int bestScore;
        private long bestScoreTime;
        private long bestTime;
        private int bestTimeScore;

        // Required for Firebase
        public MemoryGameStats() {
        }

        public int getBestScore() {
            return bestScore;
        }

        public void setBestScore(int bestScore) {
            this.bestScore = bestScore;
        }

        public long getBestScoreTime() {
            return bestScoreTime;
        }

        public void setBestScoreTime(long bestScoreTime) {
            this.bestScoreTime = bestScoreTime;
        }

        public long getBestTime() {
            return bestTime;
        }

        public void setBestTime(long bestTime) {
            this.bestTime = bestTime;
        }

        public int getBestTimeScore() {
            return bestTimeScore;
        }

        public void setBestTimeScore(int bestTimeScore) {
            this.bestTimeScore = bestTimeScore;
        }
    }

    // PairMatchingStats model class
    public static class PairMatchingStats {
        private int bestMoves;
        private long bestTime;
        private int fastestMoves;
        private long fastestTime;

        // Required for Firebase
        public PairMatchingStats() {
        }

        public int getBestMoves() {
            return bestMoves;
        }

        public void setBestMoves(int bestMoves) {
            this.bestMoves = bestMoves;
        }

        public long getBestTime() {
            return bestTime;
        }

        public void setBestTime(long bestTime) {
            this.bestTime = bestTime;
        }

        public int getFastestMoves() {
            return fastestMoves;
        }

        public void setFastestMoves(int fastestMoves) {
            this.fastestMoves = fastestMoves;
        }

        public long getFastestTime() {
            return fastestTime;
        }

        public void setFastestTime(long fastestTime) {
            this.fastestTime = fastestTime;
        }
    }
}





package com.example.memorymaster;

import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.FirebaseFirestore;

public class LoginActivity extends AppCompatActivity {

    private EditText editTextEmail, editTextPassword;
    private Button buttonLogin;
    private TextView textViewRegisterLink;

    private FirebaseAuth mAuth;
    private FirebaseFirestore db;

    private SoundManager soundManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        editTextEmail = findViewById(R.id.editTextLoginEmail);
        editTextPassword = findViewById(R.id.editTextLoginPassword);
        buttonLogin = findViewById(R.id.buttonLogin);
        textViewRegisterLink = findViewById(R.id.textViewRegisterLink);

        mAuth = FirebaseAuth.getInstance();
        db = FirebaseFirestore.getInstance();

        soundManager = SoundManager.getInstance(this);

        buttonLogin.setOnClickListener(v -> loginUser());

        textViewRegisterLink.setOnClickListener(v -> {
            Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
            startActivity(intent);
            finish();
        });
    }

    private void loginUser() {
        String email = editTextEmail.getText().toString().trim();
        String password = editTextPassword.getText().toString().trim();

        // Validate inputs
        if (TextUtils.isEmpty(email)) {
            editTextEmail.setError("Введите email");
            return;
        }

        if (TextUtils.isEmpty(password)) {
            editTextPassword.setError("Введите пароль");
            return;
        }

        // Sign in with Firebase Authentication
        mAuth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, task -> {
                    if (task.isSuccessful()) {
                        soundManager.playButtonClickSound();
                        // Sign-in success, navigate to MainActivity
                        Toast.makeText(LoginActivity.this, "Вход выполнен успешно!", Toast.LENGTH_SHORT).show();
                        Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                        startActivity(intent);
                        finish();
                    } else {
                        soundManager.playWrongSound();
                        // If sign in fails, display a message to the user.
                        Toast.makeText(LoginActivity.this, "Ошибка входа: " + task.getException().getMessage(),
                                Toast.LENGTH_LONG).show();
                    }
                });
    }
}





package com.example.memorymaster;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.google.firebase.FirebaseApp;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;

public class MainActivity extends AppCompatActivity {

    private SoundManager soundManager;
    private FirebaseAuth auth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize Firebase
        FirebaseApp.initializeApp(this);

        auth = FirebaseAuth.getInstance();

        soundManager = SoundManager.getInstance(this);

        Button btnStartMemoryGame = findViewById(R.id.btn_memory);
        Button btnStartReactionGame = findViewById(R.id.btn_reaction);
        Button btnProfile = findViewById(R.id.btn_profile);
        Button btnStartPairMatchingGame = findViewById(R.id.btn_pair_matching);

        btnStartMemoryGame.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            Intent intent = new Intent(MainActivity.this, DifficultySelectionActivity.class);
            startActivity(intent);
        });

        btnStartReactionGame.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            Intent intent = new Intent(MainActivity.this, ReactionGameModeSelection.class);
            startActivity(intent);
        });

        btnProfile.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            Intent intent = new Intent(MainActivity.this, ProfileActivity.class);
            startActivity(intent);
        });

        btnStartPairMatchingGame.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            Intent intent = new Intent(MainActivity.this, PairMatchingGame.class);
            startActivity(intent);
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        FirebaseUser currentUser = auth.getCurrentUser();

        // If user is not logged in, navigate to LoginActivity
        if (currentUser == null) {
            Intent intent = new Intent(MainActivity.this, LoginActivity.class);
            startActivity(intent);
            finish();  // Close MainActivity to prevent returning to it
        } else {
            // Optionally, retrieve user's nickname and store in SharedPreferences
            SharedPreferences preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
            SharedPreferences.Editor editor = preferences.edit();
            editor.putString("nickname", currentUser.getDisplayName()); // Assuming you set display name on registration
            editor.apply();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (soundManager != null) {
            soundManager.pauseBackgroundMusic();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (soundManager != null) {
            soundManager.release();
        }
    }
}




package com.example.memorymaster;


public class MemoryCard {
    private final int value;

    public MemoryCard(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}





package com.example.memorymaster;

import android.app.Activity;
import android.content.Intent;
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 android.app.AlertDialog;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.widget.Toast;

import androidx.constraintlayout.widget.ConstraintLayout;

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

import java.util.ArrayList;
import java.util.Random;

public class MemoryGame extends Activity {

    private GridLayout gridLayout;
    private ArrayList<Button> squares;
    private TextView title;
    private TextView level;
    private TextView scoreText;
    private Button startButton;
    private Button nextLevelButton;
    private ArrayList<Button> correctSeq;
    private int currentLevel;
    private int currentScore;
    private int userIndex;
    private int tileCount;
    private String difficulty;
    private Handler handler;
    private SharedPreferences preferences;
    private String playerNickname;
    private int attemptsLeft = 3;
    private int currentTilePoints = 50;
    private SoundManager soundManager;

    private TextView timerTextView;
    private Handler timerHandler = new Handler();
    private Runnable timerRunnable;
    private long startTime;
    private long endTime;

    private final int COLOR_BACKGROUND = Color.parseColor("#E6E6FA");
    private final int COLOR_BUTTON_NORMAL = Color.parseColor("#800080");
    private final int COLOR_BUTTON_FLASH = Color.parseColor("#008000");
    private final int COLOR_BUTTON_CORRECT = Color.parseColor("#0000FF");
    private final int COLOR_BUTTON_WRONG = Color.parseColor("#F44336");
    private final int COLOR_TEXT = Color.parseColor("#FFA500");
    private final int COLOR_TILE_NORMAL = Color.parseColor("#FFFFFF");
    private boolean gameStarted = false;
    private KonfettiView konfettiView;

    private FirebaseHelper firebaseHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_game);
        konfettiView = findViewById(R.id.konfettiView);

        soundManager = SoundManager.getInstance(this);
        gridLayout = findViewById(R.id.gridLayout);

        tileCount = getIntent().getIntExtra("tileCount", 9);
        difficulty = getDifficultyLabel(tileCount);

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

        ConstraintLayout mainLayout = findViewById(R.id.mainLayout);
        mainLayout.setBackgroundColor(COLOR_BACKGROUND);

        title = findViewById(R.id.title);
        level = findViewById(R.id.level);
        scoreText = findViewById(R.id.score);
        startButton = findViewById(R.id.startBtn);
        nextLevelButton = findViewById(R.id.nextLevelBtn);
        timerTextView = findViewById(R.id.timerTextView);

        startButton.setOnClickListener(v -> startGame());
        nextLevelButton.setOnClickListener(v -> startGame());
        nextLevelButton.setVisibility(View.GONE);

        correctSeq = new ArrayList<>();
        currentLevel = 1;
        currentScore = 0;
        handler = new Handler();

        firebaseHelper = new FirebaseHelper();

        setupUI();
        setupGameGrid();
        updateUI();

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

                timerTextView.setText(String.format("%d:%02d", minutes, seconds));
                timerHandler.postDelayed(this, 500);
            }
        };
        startButton.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            startGame();
        });
        nextLevelButton.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            startGame();
        });
    }

    private void setupUI() {
        title.setTextColor(COLOR_TEXT);
        level.setTextColor(COLOR_TEXT);
        scoreText.setTextColor(COLOR_TEXT);

        startButton.setBackgroundColor(COLOR_BUTTON_NORMAL);
        startButton.setTextColor(COLOR_TEXT);

        nextLevelButton.setBackgroundColor(COLOR_BUTTON_NORMAL);
        nextLevelButton.setTextColor(COLOR_TEXT);
    }

    private String getDifficultyLabel(int tileCount) {
        switch (tileCount) {
            case 4:
                return "easy";
            case 9:
                return "medium";
            case 12:
                return "hard";
            default:
                return "medium";
        }
    }

    private void setupGameGrid() {
        squares = new ArrayList<>();
        gridLayout.removeAllViews();

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

        int screenWidth = getResources().getDisplayMetrics().widthPixels;
        int screenHeight = getResources().getDisplayMetrics().heightPixels;

        int availableWidth = screenWidth - 40;
        int availableHeight = screenHeight - 300;

        int tileSize = Math.min(availableWidth / columnCount, availableHeight / columnCount) - 10;

        for (int i = 0; i < tileCount; i++) {
            Button button = new Button(this);
            button.setId(View.generateViewId());

            GridLayout.LayoutParams params = new GridLayout.LayoutParams();
            params.width = tileSize;
            params.height = tileSize;
            params.setMargins(5, 5, 5, 5);
            button.setLayoutParams(params);

            button.setBackgroundColor(COLOR_TILE_NORMAL);
            button.setTextColor(COLOR_TEXT);
            button.setTextSize(20);

            button.setOnClickListener(this::checkSequence);
            squares.add(button);
            gridLayout.addView(button);
        }
    }

    private void resetTileColors() {
        for (Button button : squares) {
            button.setBackgroundColor(COLOR_TILE_NORMAL);
            button.setText("");
        }
    }

    private void startGame() {
        gameStarted = true;
        isCheckingSequence = false;
        if (currentLevel == 1) {
            startTime = System.currentTimeMillis();
        }
        timerHandler.postDelayed(timerRunnable, 0);
        nextLevelButton.setVisibility(View.GONE);
        if (currentLevel > 2) { // Adjust based on desired levels
            showVictoryMessage();
            return;
        }

        correctSeq.clear();
        userIndex = 0;
        currentTilePoints = 50;
        title.setText("Запоминай последовательность!");
        level.setText("Уровень " + currentLevel + " | Попыток: " + attemptsLeft);
        disableSquares();
        startButton.setVisibility(View.INVISIBLE);

        resetTileColors();
        showPattern();
    }

    private void showPattern() {
        disableSquares();
        int delay = preferences.getInt("sequence_speed", 700); // Ensure a default value

        for (int i = 0; i < currentLevel; i++) {
            int finalI = i;
            handler.postDelayed(() -> {
                Random random = new Random();
                int randomIndex = random.nextInt(squares.size());
                Button randomSq = squares.get(randomIndex);
                flashButton(randomSq, false);
                correctSeq.add(randomSq);

                if (finalI == currentLevel - 1) {
                    handler.postDelayed(this::playerTurn, 700);
                }
            }, delay);
            delay += preferences.getInt("sequence_speed", 700);
        }
    }

    private void flashButton(Button button, boolean isPlayerTurn) {
        int color = isPlayerTurn ? COLOR_BUTTON_CORRECT : COLOR_BUTTON_FLASH;
        animateButton(button, color, "");
        soundManager.playButtonClickSound();
    }

    private void playerTurn() {
        enableSquares();
        title.setText("Повтори последовательность");
    }

    private boolean isCheckingSequence = false;
    private void checkSequence(View view) {
        if (!gameStarted || isCheckingSequence) {
            return;
        }

        soundManager.playButtonClickSound();
        isCheckingSequence = true;
        Button clickedButton = (Button) view;

        if (clickedButton == correctSeq.get(userIndex)) {
            soundManager.playCorrectSound();
            animateButton(clickedButton, COLOR_BUTTON_CORRECT, "✔");

            userIndex++;
            level.setText(userIndex + "/" + currentLevel);

            currentScore += currentTilePoints;
            currentTilePoints += 25;

            updateUI();

            if (userIndex == currentLevel) {
                currentLevel++;
                soundManager.playLevelUpSound();
                handler.postDelayed(this::startNextLevel, 1000);
            } else {
                handler.postDelayed(() -> {
                    isCheckingSequence = false;
                    enableSquares();
                }, 500);
            }
        } else {
            soundManager.playWrongSound();
            animateButton(clickedButton, COLOR_BUTTON_WRONG, "✖");

            currentScore = 0;
            updateUI();

            attemptsLeft--;
            if (attemptsLeft > 0) {
                handler.postDelayed(this::resetLevel, 1000);
            } else {
                handler.postDelayed(this::gameOver, 1000);
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (gameStarted) {
            timerHandler.postDelayed(timerRunnable, 0);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        timerHandler.removeCallbacks(timerRunnable);
    }

    private void startNextLevel() {
        title.setText("Уровень пройден! Готовы к следующему?");
        nextLevelButton.setVisibility(View.VISIBLE);
        disableSquares();
        isCheckingSequence = false;
    }

    private void resetLevel() {
        title.setText("Новая последовательность! Попыток: " + attemptsLeft);
        disableSquares();
        isCheckingSequence = false;
        handler.postDelayed(this::startGame, 1000);
    }

    private void gameOver() {
        gameStarted = false;
        isCheckingSequence = false;
        timerHandler.removeCallbacks(timerRunnable);
        title.setText("Игра окончена!");

        endTime = System.currentTimeMillis();
        long totalTime = (endTime - startTime) / 1000;

        disableSquares();
        startButton.setVisibility(View.VISIBLE);

        // Update Firestore
        firebaseHelper.updateMemoryGameStats(playerNickname, difficulty, currentScore, totalTime, new FirebaseHelper.UpdateStatsCallback() {
            @Override
            public void onUpdateCompleted(boolean newRecord) {
                if (newRecord) {
                    soundManager.playNewRecordSound();
                    showConfetti();
                } else {
                    soundManager.playDefeatSound();
                }
                showGameOverDialog(newRecord);
            }

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

    private void showGameOverDialog(boolean isNewRecord) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AlertDialogCustom);
        String message = isNewRecord ?
                "Поздравляем! Вы установили новый рекорд!\n" :
                "Вы исчерпали все попытки. ";
        message += "Хотите попробовать снова или вернуться в главное меню?";

        builder.setTitle("Игра окончена!")
                .setMessage(message)
                .setPositiveButton("Попробовать снова", (dialog, which) -> {
                    currentLevel = 1;
                    currentScore = 0;
                    currentTilePoints = 50;
                    attemptsLeft = 3;
                    startGame();
                })
                .setNegativeButton("Главное меню", (dialog, which) -> {
                    Intent intent = new Intent(MemoryGame.this, MainActivity.class);
                    startActivity(intent);
                    finish();
                })
                .setCancelable(false)
                .show();
    }

    private void showVictoryMessage() {
        endTime = System.currentTimeMillis();
        long totalTime = (endTime - startTime) / 1000;

        firebaseHelper.updateMemoryGameStats(playerNickname, difficulty, currentScore, totalTime, new FirebaseHelper.UpdateStatsCallback() {
            @Override
            public void onUpdateCompleted(boolean isNewRecord) {
                if (isNewRecord) {
                    soundManager.playNewRecordSound();
                    showConfetti();
                } else {
                    soundManager.playVictorySound();
                }

                AlertDialog.Builder builder = new AlertDialog.Builder(MemoryGame.this, R.style.AlertDialogCustom);
                String message = isNewRecord ?
                        "Поздравляем! Вы установили новый рекорд!\n" :
                        "Вы успешно прошли игру!\n";

                builder.setTitle("Поздравляем!")
                        .setMessage(message)
                        .setPositiveButton("Назад в главное меню", (dialog, which) -> {
                            Intent intent = new Intent(MemoryGame.this, MainActivity.class);
                            startActivity(intent);
                            finish();
                        })
                        .setCancelable(false)
                        .show();
            }

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

    private void updateUI() {
        scoreText.setText("Очки: " + currentScore);
        level.setText("Уровень: " + currentLevel + " | Попыток: " + attemptsLeft);
    }

    private void enableSquares() {
        for (Button square : squares) {
            square.setClickable(true);
        }
    }

    private void disableSquares() {
        for (Button square : squares) {
            square.setClickable(false);
        }
    }

    private void animateButton(final Button button, int color, String text) {
        button.setBackgroundColor(color);
        button.setText(text);

        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1f, 1.2f, 1f);
        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f, 1.2f, 1f);
        ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(button, scaleX, scaleY);
        scaleAnimator.setDuration(300);
        scaleAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

        scaleAnimator.start();

        handler.postDelayed(() -> {
            button.setBackgroundColor(COLOR_TILE_NORMAL);
            button.setText("");
        }, 500);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        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);
    }
}





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 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 long pauseTime = 0;
    private SoundManager soundManager;
    private KonfettiView konfettiView;

    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();

        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); // Ensure you have card_back drawable

            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); // Ensure you have card_front drawable

        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 + " (Total: " + totalMoves + ")");
    }

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

    private void pauseTimer() {
        timerHandler.removeCallbacks(timerRunnable);
        pauseTime = System.currentTimeMillis() - startTime;
    }

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

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

    private void onGameWon() {
        pauseTimer();
        totalTime += pauseTime / 1000;

        if (currentLevel >= 3) { // Adjust based on desired levels
            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);
                })
                .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() {
        // Update Firestore
        firebaseHelper.updatePairMatchingStats(nickname, 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;
        pauseTime = 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);
    }
}




package com.example.memorymaster;

import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.firestore.*;

public class ProfileActivity extends AppCompatActivity {

    private TextView textViewNickname, textViewEmail;
    private Button buttonLogout;

    private FirebaseAuth mAuth;
    private FirebaseFirestore db;

    private SoundManager soundManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_profile);

        textViewNickname = findViewById(R.id.textViewNickname);
        textViewEmail = findViewById(R.id.textViewEmail);
        buttonLogout = findViewById(R.id.buttonLogout);

        mAuth = FirebaseAuth.getInstance();
        db = FirebaseFirestore.getInstance();

        soundManager = SoundManager.getInstance(this);

        loadUserProfile();

        buttonLogout.setOnClickListener(v -> logout());
    }

    private void loadUserProfile() {
        FirebaseUser currentUser = mAuth.getCurrentUser();
        if (currentUser == null) {
            // User is not logged in, navigate back to LoginActivity
            Intent intent = new Intent(ProfileActivity.this, LoginActivity.class);
            startActivity(intent);
            finish();
            return;
        }

        String userId = currentUser.getUid();

        db.collection("users").document(userId)
                .get()
                .addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
                        DocumentSnapshot document = task.getResult();
                        if (document.exists()) {
                            String nickname = document.getString("nickname");
                            String email = document.getString("email");

                            textViewNickname.setText("Никнейм: " + (nickname != null ? nickname : "Не установлено"));
                            textViewEmail.setText("Email: " + (email != null ? email : "Не установлено"));
                        } else {
                            Toast.makeText(ProfileActivity.this, "Данные пользователя не найдены", Toast.LENGTH_LONG).show();
                        }
                    } else {
                        Toast.makeText(ProfileActivity.this, "Ошибка: " + task.getException().getMessage(), Toast.LENGTH_LONG).show();
                    }
                });
    }

    private void logout() {
        mAuth.signOut();
        soundManager.playButtonClickSound();
        Intent intent = new Intent(ProfileActivity.this, LoginActivity.class);
        startActivity(intent);
        finish();
    }
}





package com.example.memorymaster;

import android.app.Activity;
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 android.widget.Toast;
import androidx.appcompat.app.AlertDialog;

import java.util.ArrayList;
import java.util.Random;

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

public class ReactionGame extends Activity {

    private GridLayout gridLayout;
    private ArrayList<Button> squares;
    private TextView title, reactionTimeText, countdownText;
    private Button startButton, retryButton;
    private Handler handler;
    private Stopwatch stopwatch;
    private int reactionTime;
    private String playerNickname;
    private int tileCount;
    private boolean gameStarted = false;
    private boolean countdownFinished = false;
    private boolean gameActive = false;
    private Button correctSquare;
    private Runnable showSquareRunnable;
    private String gameMode;
    private int currentLevel = 1;
    private int[] levelTargets = {1000, 900, 800, 700, 600, 550, 500, 450, 400, 350};
    private SoundManager soundManager;
    private KonfettiView konfettiView;

    private FirebaseHelper firebaseHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_reaction_game);
        konfettiView = findViewById(R.id.konfettiView);
        gridLayout = findViewById(R.id.gridLayout);

        initializeViews();
        initializeGame();

        gameMode = getIntent().getStringExtra("gameMode");
        if ("level".equals(gameMode)) {
            startLevelMode();
        } else {
            startFreeMode();
        }

        setupListeners();
    }

    private void initializeViews() {
        title = findViewById(R.id.title);
        reactionTimeText = findViewById(R.id.reactionTime);
        countdownText = findViewById(R.id.countdownText);
        startButton = findViewById(R.id.startBtn);
        retryButton = findViewById(R.id.retryBtn);
        gridLayout = findViewById(R.id.gridLayout);
    }

    private void initializeGame() {
        tileCount = getIntent().getIntExtra("tileCount", 1);
        SharedPreferences preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
        playerNickname = preferences.getString("nickname", "Player");
        stopwatch = new Stopwatch();
        handler = new Handler();
        soundManager = SoundManager.getInstance(this);

        setupGameGrid();

        firebaseHelper = new FirebaseHelper();
    }

    private void startLevelMode() {
        updateLevelTitle();
    }

    private void startFreeMode() {
        title.setText("Свободный режим: Проверьте вашу реакцию");
    }

    private void setupListeners() {
        startButton.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            gameActive = true;
            startCountdown();
            startButton.setVisibility(View.GONE);
        });

        retryButton.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            gameActive = true;
            retryButton.setVisibility(View.GONE);
            startCountdown();
        });

        setAllSquaresClickable(true);
    }



    private void setAllSquaresClickable(boolean clickable) {
        for (Button square : squares) {
            square.setClickable(clickable);
        }
    }
    private void setupGameGrid() {
        squares = new ArrayList<>();
        gridLayout.removeAllViews();

        int columnCount = tileCount == 2 ? 2 : (int) Math.sqrt(tileCount);
        gridLayout.setColumnCount(columnCount);

        int screenWidth = getResources().getDisplayMetrics().widthPixels;
        int tileSize = (screenWidth - 60) / columnCount;

        for (int i = 0; i < tileCount; i++) {
            Button button = new Button(this);
            GridLayout.LayoutParams params = new GridLayout.LayoutParams();
            params.width = tileSize;
            params.height = tileSize;
            params.setMargins(5, 5, 5, 5);
            button.setLayoutParams(params);
            button.setBackgroundColor(Color.WHITE);
            button.setOnClickListener(this::checkReaction);
            squares.add(button);
            gridLayout.addView(button);
        }
    }

    private void startCountdown() {
        countdownText.setVisibility(View.VISIBLE);
        gameStarted = false;
        countdownFinished = false;

        handler.postDelayed(() -> countdownText.setText("3"), 0);
        handler.postDelayed(() -> countdownText.setText("2"), 1000);
        handler.postDelayed(() -> countdownText.setText("1"), 2000);

        handler.postDelayed(() -> {
            countdownText.setText("");
            countdownText.setVisibility(View.GONE);
            countdownFinished = true;
            startReactionGame();
        }, 3000);
    }

    private void startReactionGame() {
        title.setText("Ждите...");
        int randomDelay = new Random().nextInt(5000 - 1000) + 1000;

        showSquareRunnable = () -> {
            showRandomSquare();
            stopwatch.start();
            gameStarted = true;
        };

        handler.postDelayed(showSquareRunnable, randomDelay);
    }

    private void showRandomSquare() {
        Random random = new Random();
        int randomIndex = random.nextInt(squares.size());
        correctSquare = squares.get(randomIndex);

        handler.post(() -> {
            correctSquare.setBackgroundColor(Color.GREEN);
            title.setText("Нажмите на зеленую плитку!");
        });
    }

    private void checkReaction(View view) {
        if (!gameActive) {
            return;
        }

        Button clickedButton = (Button) view;

        if (!countdownFinished) {
            // Player clicked too early
            title.setText("Слишком рано! Вы проиграли.");
            soundManager.playWrongSound();
            resetGame();
            return;
        }

        if (!gameStarted) {
            title.setText("Слишком рано! Подождите зеленую плитку.");
            soundManager.playWrongSound();
            resetGame();
            return;
        }

        if (clickedButton != correctSquare) {
            title.setText("Ошибка! Нажата неверная плитка.");
            soundManager.playWrongSound();
            resetGame();
            return;
        }

        stopwatch.stop();
        reactionTime = (int) stopwatch.getElapsedTimeMillis();
        reactionTimeText.setText("Время реакции: " + reactionTime + " мс");

        clickedButton.setBackgroundColor(Color.BLUE);
        new Handler().postDelayed(() -> {
            clickedButton.setBackgroundColor(Color.WHITE);
        }, 200);

        if ("level".equals(gameMode)) {
            checkLevelCompletion(reactionTime);
        } else {
            evaluateReactionTime(reactionTime);
            retryButton.setVisibility(View.VISIBLE);
        }

        saveBestTime();
        gameActive = false;
    }

    private void evaluateReactionTime(int reactionTime) {
        if (reactionTime <= 200) {
            title.setText("Отличная реакция!");
        } else if (reactionTime <= 300) {
            title.setText("Хорошая реакция!");
        } else if (reactionTime <= 500) {
            title.setText("Средняя реакция.");
        } else {
            title.setText("Можно быстрее!");
        }
    }

    private void checkLevelCompletion(int reactionTime) {
        if (reactionTime <= levelTargets[currentLevel - 1]) {
            if (currentLevel >= levelTargets.length) {
                gameCompleted();
            } else {
                levelCompleted();
            }
        } else {
            // Show dialog for level failure
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Уровень не пройден")
                    .setMessage("Вы не уложились в " + levelTargets[currentLevel - 1] + " мс")
                    .setCancelable(false)
                    .setPositiveButton("Начать с первого уровня", (dialog, which) -> {
                        soundManager.playButtonClickSound();
                        currentLevel = 1;
                        updateLevelTitle();
                        gameActive = true;
                        startCountdown();
                    })
                    .setNegativeButton("Главное меню", (dialog, which) -> {
                        soundManager.playButtonClickSound();
                        finish(); // Return to main menu
                    });
            AlertDialog alert = builder.create();
            alert.show();
        }
        resetGame();
    }

    private void levelCompleted() {
        soundManager.playLevelUpSound();
        title.setText("Уровень " + currentLevel + " пройден!");
        startButton.setText("Следующий уровень");
        startButton.setVisibility(View.VISIBLE);
        retryButton.setVisibility(View.GONE);
        gameActive = false;
    }

    private void gameCompleted() {
        // Update Firestore
        firebaseHelper.updateReactionGameStats(playerNickname, reactionTime, new FirebaseHelper.UpdateStatsCallback() {
            @Override
            public void onUpdateCompleted(boolean newRecord) {
                if (newRecord) {
                    soundManager.playNewRecordSound();
                    showConfetti();
                } else {
                    soundManager.playVictorySound();
                }

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

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

    private void saveBestTime() {
        firebaseHelper.updateReactionGameStats(playerNickname, reactionTime, new FirebaseHelper.UpdateStatsCallback() {
            @Override
            public void onUpdateCompleted(boolean newRecord) {
                if (newRecord) {
                    soundManager.playNewRecordSound();
                    showConfetti();
                    title.setText(title.getText() + " Новый рекорд!");
                }
            }

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

    private void resetGame() {
        handler.removeCallbacks(showSquareRunnable);

        for (Button square : squares) {
            square.setBackgroundColor(Color.WHITE);
        }
        gameStarted = false;
        countdownFinished = false;
        gameActive = false;

        if ("level".equals(gameMode)) {
            startButton.setVisibility(View.VISIBLE);
            startButton.setText("Начать уровень");
        } else {
            retryButton.setVisibility(View.VISIBLE);
        }

        if (stopwatch.isRunning()) {
            stopwatch.stop();
        }

        setAllSquaresClickable(true);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        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);
    }

    private void updateLevelTitle() {
        title.setText("Уровень " + currentLevel + ": Реагируйте быстрее чем " + levelTargets[currentLevel - 1] + " мс");
    }


}




package com.example.memorymaster;


import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;

public class ReactionGameModeSelection extends Activity {
    private SoundManager soundManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_reaction_game_mode_selection);

        soundManager = SoundManager.getInstance(this);

        Button btnFreeMode = findViewById(R.id.btn_free_mode);
        Button btnLevelMode = findViewById(R.id.btn_level_mode);

        // Вместо непосредственного запуска игры, переходим на экран выбора плиток
        btnFreeMode.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            Intent intent = new Intent(ReactionGameModeSelection.this, TileSelectionActivity.class);
            intent.putExtra("gameMode", "free");
            startActivity(intent);
        });

        btnLevelMode.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            Intent intent = new Intent(ReactionGameModeSelection.this, TileSelectionActivity.class);
            intent.putExtra("gameMode", "level");
            startActivity(intent);
        });
    }
}





package com.example.memorymaster;

import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.firestore.FirebaseFirestore;

import java.util.HashMap;
import java.util.Map;

public class RegisterActivity extends AppCompatActivity {

    private EditText editTextNickname, editTextEmail, editTextPassword;
    private Button buttonRegister;
    private TextView textViewLoginLink;

    private FirebaseAuth mAuth;
    private FirebaseFirestore db;

    private SoundManager soundManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);

        editTextNickname = findViewById(R.id.editTextNickname);
        editTextEmail = findViewById(R.id.editTextEmail);
        editTextPassword = findViewById(R.id.editTextPassword);
        buttonRegister = findViewById(R.id.buttonRegister);
        textViewLoginLink = findViewById(R.id.textViewLoginLink);

        mAuth = FirebaseAuth.getInstance();
        db = FirebaseFirestore.getInstance();

        soundManager = SoundManager.getInstance(this);

        buttonRegister.setOnClickListener(v -> registerUser());

        textViewLoginLink.setOnClickListener(v -> {
            Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
            startActivity(intent);
            finish();
        });
    }

    private void registerUser() {
        String nickname = editTextNickname.getText().toString().trim();
        String email = editTextEmail.getText().toString().trim();
        String password = editTextPassword.getText().toString().trim();

        // Validate inputs
        if (TextUtils.isEmpty(nickname)) {
            editTextNickname.setError("Введите никнейм");
            return;
        }

        if (TextUtils.isEmpty(email)) {
            editTextEmail.setError("Введите email");
            return;
        }

        if (TextUtils.isEmpty(password)) {
            editTextPassword.setError("Введите пароль");
            return;
        }

        if (password.length() < 6) {
            editTextPassword.setError("Пароль должен быть не менее 6 символов");
            return;
        }

        // Check if nickname is unique
        db.collection("users")
                .whereEqualTo("nickname", nickname)
                .get()
                .addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
                        if (!task.getResult().isEmpty()) {
                            editTextNickname.setError("Этот никнейм уже занят");
                            Toast.makeText(RegisterActivity.this, "Никнейм уже занят. Выберите другой.", Toast.LENGTH_LONG).show();
                        } else {
                            // Proceed with registration
                            createUser(nickname, email, password);
                        }
                    } else {
                        Toast.makeText(RegisterActivity.this, "Ошибка проверки никнейма: " + task.getException().getMessage(), Toast.LENGTH_LONG).show();
                    }
                });
    }

    private void createUser(String nickname, String email, String password) {
        mAuth.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, task -> {
                    if (task.isSuccessful()) {
                        soundManager.playButtonClickSound();
                        // Sign-up success, now save additional user data to Firestore
                        String userId = mAuth.getCurrentUser().getUid();
                        Map<String, Object> user = new HashMap<>();
                        user.put("nickname", nickname);
                        user.put("email", email);
                        user.put("userId", userId);

                        db.collection("users").document(userId)
                                .set(user)
                                .addOnSuccessListener(aVoid -> {
                                    // Set display name
                                    mAuth.getCurrentUser().updateProfile(new com.google.firebase.auth.UserProfileChangeRequest.Builder()
                                                    .setDisplayName(nickname)
                                                    .build())
                                            .addOnCompleteListener(taskUpdate -> {
                                                if (taskUpdate.isSuccessful()) {
                                                    Toast.makeText(RegisterActivity.this, "Регистрация успешна!", Toast.LENGTH_SHORT).show();
                                                    // Navigate to MainActivity
                                                    Intent intent = new Intent(RegisterActivity.this, MainActivity.class);
                                                    startActivity(intent);
                                                    finish();
                                                } else {
                                                    Toast.makeText(RegisterActivity.this, "Ошибка установки никнейма: " + taskUpdate.getException().getMessage(), Toast.LENGTH_LONG).show();
                                                }
                                            });
                                })
                                .addOnFailureListener(e -> {
                                    Toast.makeText(RegisterActivity.this, "Ошибка сохранения данных: " + e.getMessage(), Toast.LENGTH_LONG).show();
                                });
                    } else {
                        soundManager.playWrongSound();
                        // If sign up fails, display a message to the user.
                        Toast.makeText(RegisterActivity.this, "Ошибка регистрации: " + task.getException().getMessage(),
                                Toast.LENGTH_LONG).show();
                    }
                });
    }
}







package com.example.memorymaster;

import android.content.Context;
import android.media.AudioAttributes;
import android.media.MediaPlayer;
import android.media.SoundPool;

public class SoundManager {
    private static SoundManager instance;
    private SoundPool soundPool;
    private MediaPlayer backgroundMusic;
    private Context context;

    private int buttonClickSound;
    private int correctSound;
    private int wrongSound;
    private int victorySound;
    private int defeatSound;
    private int newRecordSound;
    private int levelUpSound;

    private boolean isMusicEnabled = true;
    private boolean isSoundEnabled = true;

    private SoundManager(Context context) {
        this.context = context.getApplicationContext();
        initializeSoundPool();
    }

    public static synchronized SoundManager getInstance(Context context) {
        if (instance == null) {
            instance = new SoundManager(context);
        } else if (instance.soundPool == null) {
            instance.initializeSoundPool();
        }
        return instance;
    }

    private void initializeSoundPool() {
        AudioAttributes audioAttributes = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_GAME)
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .build();

        soundPool = new SoundPool.Builder()
                .setMaxStreams(6)
                .setAudioAttributes(audioAttributes)
                .build();

        buttonClickSound = soundPool.load(context, R.raw.button_click, 1);
        correctSound = soundPool.load(context, R.raw.correct_sound, 1);
        wrongSound = soundPool.load(context, R.raw.wrong_sound, 1);
        victorySound = soundPool.load(context, R.raw.victory, 1);
        defeatSound = soundPool.load(context, R.raw.defeat, 1);
        newRecordSound = soundPool.load(context, R.raw.new_record, 1);
        levelUpSound = soundPool.load(context, R.raw.level_up, 1);

        initializeBackgroundMusic();
    }

    private void initializeBackgroundMusic() {
        backgroundMusic = MediaPlayer.create(context, R.raw.background_music);
        backgroundMusic.setLooping(true);
    }

    public void playButtonClickSound() {
        playSoundEffect(buttonClickSound);
    }

    public void playCorrectSound() {
        playSoundEffect(correctSound);
    }

    public void playWrongSound() {
        playSoundEffect(wrongSound);
    }

    public void playVictorySound() {
        playSoundEffect(victorySound);
    }

    public void playDefeatSound() {
        playSoundEffect(defeatSound);
    }

    public void playNewRecordSound() {
        playSoundEffect(newRecordSound);
    }

    public void playLevelUpSound() {
        playSoundEffect(levelUpSound);
    }

    private void playSoundEffect(int soundId) {
        if (isSoundEnabled && soundPool != null) {
            soundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);
        }
    }

    public void playBackgroundMusic() {
        if (isMusicEnabled && backgroundMusic != null && !backgroundMusic.isPlaying()) {
            backgroundMusic.start();
        }
    }

    public void pauseBackgroundMusic() {
        if (backgroundMusic != null && backgroundMusic.isPlaying()) {
            backgroundMusic.pause();
        }
    }

    public void stopBackgroundMusic() {
        if (backgroundMusic != null) {
            backgroundMusic.stop();
            backgroundMusic.prepareAsync();
        }
    }

    public void setMusicEnabled(boolean enabled) {
        isMusicEnabled = enabled;
        if (enabled) {
            playBackgroundMusic();
        } else {
            pauseBackgroundMusic();
        }
    }

    public void setSoundEnabled(boolean enabled) {
        isSoundEnabled = enabled;
    }

    public void release() {
        if (soundPool != null) {
            soundPool.release();
            soundPool = null;
        }
        if (backgroundMusic != null) {
            backgroundMusic.release();
            backgroundMusic = null;
        }
    }
}





package com.example.memorymaster;

public class Stopwatch {
    private long startTime;
    private long stopTime;
    private boolean running;

    public void start() {
        this.startTime = System.currentTimeMillis();
        this.running = true;
    }

    public void stop() {
        this.stopTime = System.currentTimeMillis();
        this.running = false;
    }

    public long getElapsedTimeNano() {
        if (running) {
            return System.nanoTime() - startTime;
        } else {
            return stopTime - startTime;
        }
    }

    public long getElapsedTimeMillis() {
        if (running) {
            return System.currentTimeMillis() - startTime;
        } else {
            return stopTime - startTime;
        }
    }

    public boolean isRunning() {
        return running;
    }
}




package com.example.memorymaster;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;

public class TileSelectionActivity extends Activity {

    private String gameMode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tile_selection);

        // Получаем режим игры из предыдущей активности
        gameMode = getIntent().getStringExtra("gameMode");

        Button btnOneTile = findViewById(R.id.btn_one_tile);
        Button btnTwoTiles = findViewById(R.id.btn_two_tiles);
        Button btnFourTiles = findViewById(R.id.btn_four_tiles);

        // Слушатели для выбора количества плиток
        btnOneTile.setOnClickListener(v -> startGame(1));
        btnTwoTiles.setOnClickListener(v -> startGame(2));
        btnFourTiles.setOnClickListener(v -> startGame(4));
    }

    // Метод для запуска игры с выбранным количеством плиток
    private void startGame(int tileCount) {
        Intent intent = new Intent(TileSelectionActivity.this, ReactionGame.class);
        // Передаем количество плиток и режим игры в ReactionGame
        intent.putExtra("tileCount", tileCount);
        intent.putExtra("gameMode", gameMode);
        startActivity(intent);

        // Закрываем текущую активность выбора
        finish();  // Закрывает TileSelectionActivity, чтобы оно не накладывалось на интерфейс игры
    }
}






activity_difficulty_selection.xml:
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#E6E6FA"
    android:padding="16dp">

    <!-- Закрепленный заголовок сверху -->
    <TextView
        android:id="@+id/select_difficulty_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Выберите сложность"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Центрируем кнопки с отступами -->
    <Button
        android:id="@+id/btn_easy"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Легкая сложность"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/select_difficulty_label"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/btn_medium"
        app:layout_constraintVertical_chainStyle="packed"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/btn_medium"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Средняя сложность"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/btn_easy"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/btn_hard"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/btn_hard"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Тяжелая сложность"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/btn_medium"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginTop="16dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>





activity_level_selection.xml:
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#E6E6FA"
    android:padding="16dp">

    <TextView
        android:id="@+id/select_level_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Выберите уровень"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginBottom="16dp"/>

    <!-- Сетка для отображения уровней -->
    <androidx.gridlayout.widget.GridLayout
        android:id="@+id/levelGrid"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:columnCount="3"
        app:layout_constraintTop_toBottomOf="@id/select_level_label"
        android:layout_marginTop="16dp"
        android:layout_marginBottom="16dp"
        app:rowCount="3" />

</androidx.constraintlayout.widget.ConstraintLayout>






activity_login.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:background="#E6E6FA">

    <TextView
        android:id="@+id/login_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Вход"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <EditText
        android:id="@+id/editTextLoginEmail"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Email"
        android:inputType="textEmailAddress"
        app:layout_constraintTop_toBottomOf="@id/login_title"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="24dp"/>

    <EditText
        android:id="@+id/editTextLoginPassword"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Пароль"
        android:inputType="textPassword"
        app:layout_constraintTop_toBottomOf="@id/editTextLoginEmail"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/buttonLogin"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Войти"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/editTextLoginPassword"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="24dp"/>

    <TextView
        android:id="@+id/textViewRegisterLink"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Нет аккаунта? Зарегистрироваться"
        android:textColor="#800080"
        app:layout_constraintTop_toBottomOf="@id/buttonLogin"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>






activity_main.xml:
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:background="#E6E6FA">

    <!-- Закрепленный заголовок сверху -->
    <TextView
        android:id="@+id/game_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Мастер памяти"
        android:textSize="30sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <!-- Центрируем кнопки -->
    <Button
        android:id="@+id/btn_memory"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Мастер памяти"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/game_title"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/btn_reaction"
        app:layout_constraintVertical_chainStyle="packed"
        android:layout_marginTop="200dp"/>

    <Button
        android:id="@+id/btn_reaction"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Игра на реакцию"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/btn_memory"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/btn_pair_matching"
        app:layout_constraintVertical_chainStyle="packed"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/btn_pair_matching"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Найди пару плиток"
        android:background="#800080"
        android:textColor="#FFA500"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@id/btn_reaction"
        app:layout_constraintBottom_toTopOf="@id/btn_profile"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintVertical_chainStyle="packed"/>


    <Button
        android:id="@+id/btn_profile"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Профиль"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/btn_pair_matching"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"
        app:layout_constraintVertical_chainStyle="packed"/>


</androidx.constraintlayout.widget.ConstraintLayout>





activity_memory_game.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Мастер памяти"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <TextView
        android:id="@+id/level"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Уровень 1"
        android:textSize="18sp"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="8dp"/>

    <TextView
        android:id="@+id/score"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Счёт: 0"
        android:textSize="18sp"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="8dp"/>

    <TextView
        android:id="@+id/timerTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0:00"
        android:textSize="18sp"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/level"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="8dp"/>

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.2" />

    <GridLayout
        android:id="@+id/gridLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/guideline"
        app:layout_constraintBottom_toTopOf="@id/startBtn"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"
        android:layout_marginBottom="16dp"/>

    <Button
        android:id="@+id/startBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Начать игру"
        android:textColor="#FFA500"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginBottom="16dp"/>

    <Button
        android:id="@+id/nextLevelBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Следующий уровень"
        android:textColor="#FFA500"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginBottom="16dp"
        android:visibility="gone" />


    <nl.dionsegijn.konfetti.KonfettiView
        android:id="@+id/konfettiView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>





activity_pair_matching_game.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#E6E6FA"
    android:padding="16dp">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Найди пару плиток"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginBottom="16dp"/>

    <TextView
        android:id="@+id/movesTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ходы: 0"
        android:textSize="18sp"
        android:textColor="#4B0082"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="8dp"/>

    <TextView
        android:id="@+id/timerTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Время: 0:00"
        android:textSize="18sp"
        android:textColor="#4B0082"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="8dp"/>

    <GridLayout
        android:id="@+id/gridLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@id/movesTextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginBottom="16dp"/>

    <nl.dionsegijn.konfetti.KonfettiView
        android:id="@+id/konfettiView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>





activity_profile.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:background="#E6E6FA">

    <TextView
        android:id="@+id/profile_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Профиль"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <TextView
        android:id="@+id/textViewNickname"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Никнейм: "
        android:textSize="18sp"
        android:textColor="#800080"
        app:layout_constraintTop_toBottomOf="@id/profile_title"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="24dp"/>

    <TextView
        android:id="@+id/textViewEmail"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Email: "
        android:textSize="18sp"
        android:textColor="#800080"
        app:layout_constraintTop_toBottomOf="@id/textViewNickname"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/buttonLogout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Выйти"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/textViewEmail"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="24dp"/>

    <!-- Optional: Add sections to display game statistics -->

</androidx.constraintlayout.widget.ConstraintLayout>





activity_reaction_game.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#E6E6FA"
    android:padding="16dp">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Игра на реакцию"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <TextView
        android:id="@+id/countdownText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="36sp"
        android:textStyle="bold"
        android:textColor="#FF0000"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"
        android:visibility="gone" />

    <TextView
        android:id="@+id/reactionTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Время реакции: 0 ms"
        android:textSize="18sp"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/countdownText"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"/>

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.3" />

    <GridLayout
        android:id="@+id/gridLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/guideline"
        app:layout_constraintBottom_toTopOf="@id/startBtn"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"
        android:layout_marginBottom="16dp"/>

    <Button
        android:id="@+id/startBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Начать игру"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginBottom="16dp"
        android:visibility="visible" />

    <Button
        android:id="@+id/retryBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ещё раз"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginBottom="16dp"
        android:visibility="gone" />

    <nl.dionsegijn.konfetti.KonfettiView
        android:id="@+id/konfettiView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>






activity_reaction_game_mode_selection.xml:
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_free_mode"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Free Mode" />

    <Button
        android:id="@+id/btn_level_mode"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Level Mode" />
</LinearLayout>







activity_register.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:background="#E6E6FA">

    <TextView
        android:id="@+id/register_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Регистрация"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <EditText
        android:id="@+id/editTextNickname"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Никнейм"
        android:inputType="textPersonName"
        app:layout_constraintTop_toBottomOf="@id/register_title"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="24dp"/>

    <EditText
        android:id="@+id/editTextEmail"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Email"
        android:inputType="textEmailAddress"
        app:layout_constraintTop_toBottomOf="@id/editTextNickname"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"/>

    <EditText
        android:id="@+id/editTextPassword"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Пароль"
        android:inputType="textPassword"
        app:layout_constraintTop_toBottomOf="@id/editTextEmail"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/buttonRegister"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Зарегистрироваться"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/editTextPassword"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="24dp"/>

    <TextView
        android:id="@+id/textViewLoginLink"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Уже есть аккаунт? Войти"
        android:textColor="#800080"
        app:layout_constraintTop_toBottomOf="@id/buttonRegister"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>






activity_tile_selection.xml:
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#E6E6FA"
    android:padding="16dp">

    <TextView
        android:id="@+id/select_difficulty_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Выберите количество плиток"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <Button
        android:id="@+id/btn_one_tile"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="1 Плитка"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/select_difficulty_label"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/btn_two_tiles"
        app:layout_constraintVertical_chainStyle="packed"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/btn_two_tiles"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="2 Плитки"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/btn_one_tile"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/btn_four_tiles"
        app:layout_constraintVertical_chainStyle="packed"
        android:layout_marginTop="16dp"/>

    <Button
        android:id="@+id/btn_four_tiles"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="4 Плитки"
        android:background="#800080"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/btn_two_tiles"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintVertical_chainStyle="packed"
        android:layout_marginTop="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>






// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.google.gms.google.services) apply false
}





plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.google.gms.google.services)
}

android {
    namespace = "com.example.memorymaster"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.memorymaster"
        minSdk = 23
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation(libs.appcompat)
    implementation(libs.material)
    implementation(libs.activity)
    implementation(libs.constraintlayout)
    implementation(libs.firebase.database)
    implementation(libs.firebase.auth)
    implementation(libs.firebase.firestore)
    testImplementation(libs.junit)
    androidTestImplementation(libs.ext.junit)
    androidTestImplementation(libs.espresso.core)
    implementation(platform("com.google.firebase:firebase-bom:33.4.0"))
    // Firebase Authentication (if necessary)
    implementation ("com.google.firebase:firebase-auth:23.0.0")

    // Firebase Realtime Database
    implementation ("com.google.firebase:firebase-database:20.0.5")
    implementation ("nl.dionsegijn:konfetti:1.3.2")
    implementation("androidx.gridlayout:gridlayout:1.0.0")
}


не получается зарегистрироваться при вводе данных выходит ошибка
Permission denied  missing or insufficient permissions