нужно сделать гант чарт по выполнению всего этого проекта я его по сути сделал за две недели остальные двое мне почти никак не помогали нужно как то оформить при этом гант чарт будто они тоже что то делали но при этом чтобы сильно сложных вопросов им нельзя было задать при сдаче а в основном я отвечал

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

    <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.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import androidx.appcompat.app.AppCompatActivity;

public class DifficultySelectionActivity extends AppCompatActivity {

    private Button btnEasy, btnMedium, btnHard;
    private ImageView lockMedium, lockHard;
    private SharedPreferences preferences;
    private Set<String> unlockedDifficulties;

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

        preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
        unlockedDifficulties = preferences.getStringSet("unlocked_difficulties", new HashSet<>(Collections.singletonList("easy")));

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

        lockMedium = findViewById(R.id.lock_medium);
        lockHard = findViewById(R.id.lock_hard);

        updateButtonStates();

        btnEasy.setOnClickListener(v -> startGameWithDifficulty(4)); // Легкая сложность - 4 плитки

        btnMedium.setOnClickListener(v -> {
            if (unlockedDifficulties.contains("medium")) {
                startGameWithDifficulty(9); // Средняя сложность - 9 плиток
            } else {
                Toast.makeText(this, "Средняя сложность заблокирована. Пройдите легкую сложность, чтобы разблокировать.", Toast.LENGTH_LONG).show();
            }
        });

        btnHard.setOnClickListener(v -> {
            if (unlockedDifficulties.contains("hard")) {
                startGameWithDifficulty(12); // Тяжелая сложность - 12 плиток
            } else {
                Toast.makeText(this, "Тяжелая сложность заблокирована. Пройдите среднюю сложность, чтобы разблокировать.", Toast.LENGTH_LONG).show();
            }
        });
    }

    private void updateButtonStates() {
        // Легкая сложность всегда разблокирована
        btnEasy.setEnabled(true);

        if (unlockedDifficulties.contains("medium")) {
            btnMedium.setEnabled(true);
            btnMedium.setBackgroundColor(getResources().getColor(R.color.button_normal));
            lockMedium.setVisibility(View.GONE);
        } else {
            btnMedium.setEnabled(false);
            btnMedium.setBackgroundColor(getResources().getColor(R.color.button_locked));
            lockMedium.setVisibility(View.VISIBLE);
        }

        if (unlockedDifficulties.contains("hard")) {
            btnHard.setEnabled(true);
            btnHard.setBackgroundColor(getResources().getColor(R.color.button_normal));
            lockHard.setVisibility(View.GONE);
        } else {
            btnHard.setEnabled(false);
            btnHard.setBackgroundColor(getResources().getColor(R.color.button_locked));
            lockHard.setVisibility(View.VISIBLE);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Обновляем состояние разблокировки
        unlockedDifficulties = preferences.getStringSet("unlocked_difficulties", new HashSet<>(Collections.singletonList("easy")));
        updateButtonStates();
    }

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






package com.example.memorymaster;

import androidx.annotation.NonNull;

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

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 userId, String difficulty, int newScore, long newTime, UpdateStatsCallback callback) {
        String path = "memory_game_statistics/" + userId + "/" + difficulty;
        DatabaseReference userStatsRef = databaseReference.child(path);

        userStatsRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                MemoryGameStats stats = snapshot.getValue(MemoryGameStats.class);
                final boolean[] newRecord = {false};
                if (stats == null) {
                    stats = new MemoryGameStats(newScore, newTime, newTime, 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;
                    }
                }
                userStatsRef.setValue(stats).addOnCompleteListener(task1 -> {
                    if (task1.isSuccessful()) {
                        if (callback != null) {
                            callback.onUpdateCompleted(newRecord[0]);
                        }
                    } else {
                        if (callback != null) {
                            callback.onUpdateFailed(task1.getException());
                        }
                    }
                });
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                if (callback != null) {
                    callback.onUpdateFailed(error.toException());
                }
            }
        });
    }

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

        userStatsRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                Integer bestReactionTime = snapshot.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);
                    }
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                if (callback != null) {
                    callback.onUpdateFailed(error.toException());
                }
            }
        });
    }

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

        userStatsRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                PairMatchingStats stats = snapshot.getValue(PairMatchingStats.class);
                final boolean[] newRecord = {false};
                if (stats == null) {
                    stats = new PairMatchingStats(moves, time, moves, 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;
                    }
                }
                userStatsRef.setValue(stats).addOnCompleteListener(task1 -> {
                    if (task1.isSuccessful()) {
                        if (callback != null) {
                            callback.onUpdateCompleted(newRecord[0]);
                        }
                    } else {
                        if (callback != null) {
                            callback.onUpdateFailed(task1.getException());
                        }
                    }
                });
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                if (callback != null) {
                    callback.onUpdateFailed(error.toException());
                }
            }
        });
    }

    // 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 MemoryGameStats(int bestScore, long bestScoreTime, long bestTime, int bestTimeScore) {
            this.bestScore = bestScore;
            this.bestScoreTime = bestScoreTime;
            this.bestTime = bestTime;
            this.bestTimeScore = bestTimeScore;
        }

        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 PairMatchingStats(int bestMoves, long bestTime, int fastestMoves, long fastestTime) {
            this.bestMoves = bestMoves;
            this.bestTime = bestTime;
            this.fastestMoves = fastestMoves;
            this.fastestTime = fastestTime;
        }

        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.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.Toast;

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

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

import java.util.Collections;
import java.util.HashSet;

public class MainActivity extends AppCompatActivity {

    private SoundManager soundManager;
    private FirebaseAuth auth;
    private DatabaseReference usersRef;

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

        SharedPreferences preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);

        if (!preferences.contains("unlocked_difficulties")) {
            // Если ключа нет, значит первый запуск, разблокируем только легкую сложность
            SharedPreferences.Editor editor = preferences.edit();
            editor.putStringSet("unlocked_difficulties", new HashSet<>(Collections.singletonList("easy")));
            editor.apply();
        }

        FirebaseApp.initializeApp(this);

        auth = FirebaseAuth.getInstance();
        usersRef = FirebaseDatabase.getInstance().getReference("users");

        soundManager = SoundManager.getInstance(this);

        ImageButton btnHelp = findViewById(R.id.button_help);
        btnHelp.setOnClickListener(v -> {
            showHelpDialog();
        });


        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 {
            String userId = currentUser.getUid();
            usersRef.child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {
                    String nickname = snapshot.child("nickname").getValue(String.class);
                    if (nickname == null) nickname = "Player";

                    SharedPreferences preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
                    SharedPreferences.Editor editor = preferences.edit();
                    editor.putString("nickname", nickname);
                    editor.apply();
                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {
                    Toast.makeText(MainActivity.this, "Ошибка загрузки профиля: " + error.getMessage(), Toast.LENGTH_LONG).show();
                }
            });
        }
    }

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

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

    private void showHelpDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AlertDialogCustom);

        LayoutInflater inflater = this.getLayoutInflater();
        View dialogView = inflater.inflate(R.layout.dialog_help, null);
        builder.setView(dialogView);

        AlertDialog alertDialog = builder.create();

        Button btnClose = dialogView.findViewById(R.id.btn_close_help);
        btnClose.setOnClickListener(v -> alertDialog.dismiss());

        alertDialog.show();
    }
}




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.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.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;

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

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class MemoryGame extends AppCompatActivity {

    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 playerUserId; // Используем userId вместо nickname
    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);

        // Инициализация SharedPreferences
        preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
        // Получаем userId из SharedPreferences или напрямую из FirebaseAuth
        FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
        if (currentUser != null) {
            playerUserId = currentUser.getUid();
        } else {
            Intent intent = new Intent(MemoryGame.this, LoginActivity.class);
            startActivity(intent);
            finish();
            return;
        }

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

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

        int maxLevel = 10;
        if (currentLevel > maxLevel) {
            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); // Теперь preferences гарантированно инициализирован

        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, "✖");

            int penaltyPoints = 50; // Штрафные очки за ошибку
            currentScore = Math.max(0, currentScore - penaltyPoints); // Уменьшаем очки, но не ниже нуля
            updateUI();

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

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

        // If user is not logged in, navigate to LoginActivity
        if (currentUser == null) {
            Intent intent = new Intent(MemoryGame.this, LoginActivity.class);
            startActivity(intent);
            finish();  // Close MemoryGame to prevent returning to it
        } else {
            // Additional logic if needed
        }
    }

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

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

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

        showDefeatDialog();
    }

    private void showDefeatDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AlertDialogCustom);
        builder.setTitle("Игра окончена!")
                .setMessage("Вы исчерпали все попытки. Хотите попробовать снова или вернуться в главное меню?")
                .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;

        // Разблокировка следующей сложности
        unlockNextDifficulty();

        firebaseHelper.updateMemoryGameStats(playerUserId, 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();
                builder.setPositiveButton("Выбрать сложность", (dialog, which) -> {
                    Intent intent = new Intent(MemoryGame.this, DifficultySelectionActivity.class);
                    startActivity(intent);
                    finish();
                });
            }

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

    private void unlockNextDifficulty() {
        SharedPreferences preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
        Set<String> unlockedDifficulties = preferences.getStringSet("unlocked_difficulties", new HashSet<>(Collections.singletonList("easy")));
        boolean updated = false;

        if ("easy".equals(difficulty) && !unlockedDifficulties.contains("medium")) {
            unlockedDifficulties.add("medium");
            updated = true;
        } else if ("medium".equals(difficulty) && !unlockedDifficulties.contains("hard")) {
            unlockedDifficulties.add("hard");
            updated = true;
        }

        if (updated) {
            SharedPreferences.Editor editor = preferences.edit();
            editor.putStringSet("unlocked_difficulties", unlockedDifficulties);
            editor.apply();
        }
    }



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

    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 com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import android.content.Intent;
import android.content.Intent;

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

public class PairMatchingGame extends AppCompatActivity {

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

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

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

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

        firebaseHelper = new FirebaseHelper();

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

        setupGameForLevel(currentLevel);
        startTimer();
    }

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

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

        setupGameGrid(numberOfCards);
        updateMovesDisplay();

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

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

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

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

        for (MemoryCard card : cardList) {
            Button button = new Button(this);
            button.setTag(card);
            button.setText("");
            button.setBackgroundResource(R.drawable.card_back); // 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(playerUserId, totalMoves, totalTime, new FirebaseHelper.UpdateStatsCallback() {
            @Override
            public void onUpdateCompleted(boolean newRecord) {
                if (newRecord) {
                    soundManager.playNewRecordSound();
                    showConfetti();
                } else {
                    soundManager.playVictorySound();
                }

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

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

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

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

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






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.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.*;

public class ProfileActivity extends AppCompatActivity {

    private TextView textViewNickname, textViewEmail;
    private TextView textViewMemoryGameStats, textViewReactionGameStats, textViewPairMatchingGameStats;
    private Button buttonLogout;

    private FirebaseAuth mAuth;
    private DatabaseReference usersRef;
    private DatabaseReference memoryGameStatsRef;
    private DatabaseReference reactionGameStatsRef;
    private DatabaseReference pairMatchingStatsRef;

    private SoundManager soundManager;

    private String userId;
    private String nickname;

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


        textViewNickname = findViewById(R.id.textViewNickname);
        textViewEmail = findViewById(R.id.textViewEmail);
        textViewMemoryGameStats = findViewById(R.id.textViewMemoryGameStats);
        textViewReactionGameStats = findViewById(R.id.textViewReactionGameStats);
        textViewPairMatchingGameStats = findViewById(R.id.textViewPairMatchingGameStats);
        buttonLogout = findViewById(R.id.buttonLogout);


        mAuth = FirebaseAuth.getInstance();
        usersRef = FirebaseDatabase.getInstance().getReference("users");
        memoryGameStatsRef = FirebaseDatabase.getInstance().getReference("memory_game_statistics");
        reactionGameStatsRef = FirebaseDatabase.getInstance().getReference("reaction_game_statistics");
        pairMatchingStatsRef = FirebaseDatabase.getInstance().getReference("pair_matching_statistics");

        soundManager = SoundManager.getInstance(this);

        loadUserProfile();

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

    private void loadUserProfile() {
        FirebaseUser currentUser = mAuth.getCurrentUser();
        if (currentUser == null) {
            // Пользователь не аутентифицирован, переходим к экрану входа
            Intent intent = new Intent(ProfileActivity.this, LoginActivity.class);
            startActivity(intent);
            finish();
            return;
        }

        userId = currentUser.getUid();

        // Загружаем основную информацию пользователя
        usersRef.child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                nickname = snapshot.child("nickname").getValue(String.class);
                String email = snapshot.child("email").getValue(String.class);

                textViewNickname.setText("Никнейм: " + (nickname != null ? nickname : "Не установлено"));
                textViewEmail.setText("Email: " + (email != null ? email : "Не установлено"));

                // После загрузки основной информации загружаем статистику
                loadUserStatistics();
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                Toast.makeText(ProfileActivity.this, "Ошибка загрузки профиля: " + error.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
    }

    private void loadUserStatistics() {
        // Загружаем статистику игры "Мастер памяти"
        memoryGameStatsRef.child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                StringBuilder memoryStatsBuilder = new StringBuilder();

                for (DataSnapshot difficultySnapshot : snapshot.getChildren()) {
                    String difficulty = difficultySnapshot.getKey();
                    FirebaseHelper.MemoryGameStats stats = difficultySnapshot.getValue(FirebaseHelper.MemoryGameStats.class);

                    if (stats != null) {
                        memoryStatsBuilder.append("Сложность: ").append(difficulty).append("\n")
                                .append("Лучший счет: ").append(stats.getBestScore()).append(" (Время: ").append(stats.getBestScoreTime()).append(" сек)\n")
                                .append("Быстрейшее время: ").append(stats.getBestTime()).append(" сек").append(" (Счет: ").append(stats.getBestTimeScore()).append(")\n\n");
                    }
                }

                if (memoryStatsBuilder.length() == 0) {
                    memoryStatsBuilder.append("Нет данных");
                }

                textViewMemoryGameStats.setText("Мастер памяти:\n" + memoryStatsBuilder.toString());
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                textViewMemoryGameStats.setText("Ошибка загрузки статистики: " + error.getMessage());
            }
        });

        // Загружаем статистику игры "Игра на реакцию"
        reactionGameStatsRef.child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                Integer bestReactionTime = snapshot.getValue(Integer.class);
                if (bestReactionTime != null) {
                    textViewReactionGameStats.setText("Игра на реакцию:\nЛучшее время реакции: " + bestReactionTime + " мс");
                } else {
                    textViewReactionGameStats.setText("Игра на реакцию:\nНет данных");
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                textViewReactionGameStats.setText("Ошибка загрузки статистики: " + error.getMessage());
            }
        });

        // Загружаем статистику игры "Найди пару плиток"
        pairMatchingStatsRef.child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                FirebaseHelper.PairMatchingStats stats = snapshot.getValue(FirebaseHelper.PairMatchingStats.class);
                if (stats != null) {
                    String statsText = "Лучшее количество ходов: " + stats.getBestMoves() + "\n" +
                            "Лучшее время: " + stats.getBestTime() + " сек\n" +
                            "Наименьшее время: " + stats.getFastestTime() + " сек\n" +
                            "Ходов за наименьшее время: " + stats.getFastestMoves();

                    textViewPairMatchingGameStats.setText("Найди пару плиток:\n" + statsText);
                } else {
                    textViewPairMatchingGameStats.setText("Найди пару плиток:\nНет данных");
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                textViewPairMatchingGameStats.setText("Ошибка загрузки статистики: " + error.getMessage());
            }
        });
    }

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

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

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 String playerUserId;
    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();
        }

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


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


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

        // Если пользователь не аутентифицирован, переходим к экрану входа
        if (currentUser == null) {
            Intent intent = new Intent(ReactionGame.this, LoginActivity.class);
            startActivity(intent);
            finish();
        }
    }
    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 during countdown
            Toast.makeText(this, "Подождите окончания отсчета!", Toast.LENGTH_SHORT).show();
            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(playerUserId, reactionTime, new FirebaseHelper.UpdateStatsCallback() {
            @Override
            public void onUpdateCompleted(boolean newRecord) {
                if (newRecord) {
                    soundManager.playNewRecordSound();
                    showConfetti();
                } else {
                    soundManager.playVictorySound();
                }

                if ("level".equals(gameMode)) {
                    unlockNextTile();
                }

                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 unlockNextTile() {
        SharedPreferences preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();

        Set<String> unlockedTiles = preferences.getStringSet("unlocked_tiles", new HashSet<>(Collections.singleton("1")));
        unlockedTiles = new HashSet<>(unlockedTiles); // Create a mutable copy

        // Add the completed tileCount to unlockedTiles
        unlockedTiles.add(String.valueOf(tileCount));

        // Determine next tile count to unlock
        int nextTileCount = getNextTileCount(tileCount);
        if (nextTileCount != -1) {
            unlockedTiles.add(String.valueOf(nextTileCount));
            Toast.makeText(this, "Разблокировано количество плиток: " + nextTileCount, Toast.LENGTH_LONG).show();
        } else {
            // All tile counts completed in level mode, unlock free mode
            editor.putBoolean("free_mode_unlocked", true);
            Toast.makeText(this, "Свободный режим разблокирован!", Toast.LENGTH_LONG).show();
        }

        editor.putStringSet("unlocked_tiles", unlockedTiles);
        editor.apply();
    }

    private int getNextTileCount(int currentTileCount) {
        int[] tileCounts = {1, 2, 4};
        for (int i = 0; i < tileCounts.length - 1; i++) {
            if (tileCounts[i] == currentTileCount) {
                return tileCounts[i+1];
            }
        }
        // Return -1 if there is no next tile count
        return -1;
    }


    private void saveBestTime() {
        firebaseHelper.updateReactionGameStats(playerUserId, 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.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.HashSet;
import java.util.Set;

public class ReactionGameModeSelection extends AppCompatActivity {
    private SoundManager soundManager;

    private Button btnFreeMode;
    private Button btnLevelMode;

    private ImageView lockFreeMode;

    private SharedPreferences preferences;
    private boolean isFreeModeUnlocked;

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

        soundManager = SoundManager.getInstance(this);

        preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
        isFreeModeUnlocked = preferences.getBoolean("free_mode_unlocked", false);

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

        lockFreeMode = findViewById(R.id.lock_free_mode);

        updateButtonStates();

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

    @Override
    protected void onResume() {
        super.onResume();
        isFreeModeUnlocked = preferences.getBoolean("free_mode_unlocked", false);
        updateButtonStates();
    }

    private void updateButtonStates() {
        if (isFreeModeUnlocked) {
            btnFreeMode.setEnabled(true);
            btnFreeMode.setBackgroundColor(getResources().getColor(R.color.button_normal));
            lockFreeMode.setVisibility(View.GONE);
        } else {
            btnFreeMode.setEnabled(false);
            btnFreeMode.setBackgroundColor(getResources().getColor(R.color.button_locked));
            lockFreeMode.setVisibility(View.VISIBLE);
        }
    }
}








package com.example.memorymaster;

import android.content.Intent;
import android.content.SharedPreferences;
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.database.*;

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 FirebaseDatabase db;
    private DatabaseReference usersRef;
    private DatabaseReference nicknamesRef;

    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 = FirebaseDatabase.getInstance();
        usersRef = db.getReference("users");
        nicknamesRef = db.getReference("nicknames");

        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
        nicknamesRef.child(nickname).addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                if (snapshot.exists()) {
                    editTextNickname.setError("Этот никнейм уже занят");
                    Toast.makeText(RegisterActivity.this, "Никнейм уже занят. Выберите другой.", Toast.LENGTH_LONG).show();
                } else {
                    // Proceed with registration
                    createUser(nickname, email, password);
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                Toast.makeText(RegisterActivity.this, "Ошибка проверки никнейма: " + error.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();
                        // Registration success
                        String userId = mAuth.getCurrentUser().getUid();
                        User user = new User(nickname, email, userId);

                        // Save user data
                        usersRef.child(userId).setValue(user)
                                .addOnSuccessListener(aVoid -> {
                                    // Save nickname in nicknames node
                                    nicknamesRef.child(nickname).setValue(userId)
                                            .addOnSuccessListener(aVoid1 -> {
                                                // Optionally 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();
                                            });
                                })
                                .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();
                    }
                });
    }

    // Класс модели пользователя
    public static class User {
        public String nickname;
        public String email;
        public String userId;

        public User() {
            // Пустой конструктор для Firebase
        }

        public User(String nickname, String email, String userId) {
            this.nickname = nickname;
            this.email = email;
            this.userId = userId;
        }
    }
}





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.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import androidx.appcompat.app.AppCompatActivity;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class TileSelectionActivity extends AppCompatActivity {

    private String gameMode;

    private Button btnOneTile;
    private Button btnTwoTiles;
    private Button btnFourTiles;

    private ImageView lockOneTile;
    private ImageView lockTwoTiles;
    private ImageView lockFourTiles;

    private SharedPreferences preferences;
    private Set<String> unlockedTiles;

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

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

        preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
        unlockedTiles = preferences.getStringSet("unlocked_tiles", new HashSet<>(Collections.singletonList("1")));

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

        lockOneTile = findViewById(R.id.lock_one_tile);
        lockTwoTiles = findViewById(R.id.lock_two_tiles);
        lockFourTiles = findViewById(R.id.lock_four_tiles);

        updateButtonStates();

        btnOneTile.setOnClickListener(v -> startGame(1));
        btnTwoTiles.setOnClickListener(v -> startGame(2));
        btnFourTiles.setOnClickListener(v -> startGame(4));
    }

    private void updateButtonStates() {
        if (unlockedTiles.contains("1")) {
            btnOneTile.setEnabled(true);
            btnOneTile.setBackgroundColor(getResources().getColor(R.color.button_normal));
            lockOneTile.setVisibility(View.GONE);
        } else {
            btnOneTile.setEnabled(false);
            btnOneTile.setBackgroundColor(getResources().getColor(R.color.button_locked));
            lockOneTile.setVisibility(View.VISIBLE);
        }

        if (unlockedTiles.contains("2")) {
            btnTwoTiles.setEnabled(true);
            btnTwoTiles.setBackgroundColor(getResources().getColor(R.color.button_normal));
            lockTwoTiles.setVisibility(View.GONE);
        } else {
            btnTwoTiles.setEnabled(false);
            btnTwoTiles.setBackgroundColor(getResources().getColor(R.color.button_locked));
            lockTwoTiles.setVisibility(View.VISIBLE);
        }

        if (unlockedTiles.contains("4")) {
            btnFourTiles.setEnabled(true);
            btnFourTiles.setBackgroundColor(getResources().getColor(R.color.button_normal));
            lockFourTiles.setVisibility(View.GONE);
        } else {
            btnFourTiles.setEnabled(false);
            btnFourTiles.setBackgroundColor(getResources().getColor(R.color.button_locked));
            lockFourTiles.setVisibility(View.VISIBLE);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        unlockedTiles = preferences.getStringSet("unlocked_tiles", new HashSet<>(Collections.singletonList("1")));
        updateButtonStates();
    }

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





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:id="@+id/difficultyLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#36342f"
    android:padding="16dp">

    <!-- Заголовок -->
    <TextView
        android:id="@+id/select_difficulty_label"
        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_easy"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Легкая"
        android:background="@color/button_normal"
        android:textColor="@color/text_color_primary"
        app:layout_constraintTop_toBottomOf="@id/select_difficulty_label"
        app:layout_constraintBottom_toTopOf="@id/btn_medium_container"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="200dp"/>

    <!-- Кнопка "Средняя сложность" с замком -->
    <FrameLayout
        android:id="@+id/btn_medium_container"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/btn_easy"
        app:layout_constraintBottom_toTopOf="@id/btn_hard_container"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp">

        <Button
            android:id="@+id/btn_medium"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Средняя"
            android:background="@color/button_normal"
            android:textColor="@color/text_color_primary"/>

        <ImageView
            android:id="@+id/lock_medium"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/ic_lock"
            android:layout_gravity="center|end"
            android:layout_marginEnd="8dp"
            android:visibility="gone"/>
    </FrameLayout>

    <!-- Кнопка "Тяжелая сложность" с замком -->
    <FrameLayout
        android:id="@+id/btn_hard_container"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/btn_medium_container"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp">

        <Button
            android:id="@+id/btn_hard"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Тяжелая"
            android:background="@color/button_normal"
            android:textColor="@color/text_color_primary"/>

        <ImageView
            android:id="@+id/lock_hard"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/ic_lock"
            android:layout_gravity="center|end"
            android:layout_marginEnd="8dp"
            android:visibility="gone"/>
    </FrameLayout>
</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:id="@+id/loginLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#36342f"
    android:padding="16dp">

    <!-- Заголовок -->
    <TextView
        android:id="@+id/login_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_constraintBottom_toTopOf="@id/editTextLoginEmail"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Поле ввода Email -->
    <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_constraintBottom_toTopOf="@id/editTextLoginPassword"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Поле ввода пароля -->
    <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_constraintBottom_toTopOf="@id/buttonLogin"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Кнопка "Войти" -->
    <Button
        android:id="@+id/buttonLogin"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Войти"
        android:background="@color/button_normal"
        android:textColor="@color/text_color_primary"
        app:layout_constraintTop_toBottomOf="@id/editTextLoginPassword"
        app:layout_constraintBottom_toTopOf="@id/textViewRegisterLink"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Ссылка на регистрацию -->
    <TextView
        android:id="@+id/textViewRegisterLink"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Нет аккаунта? Зарегистрироваться"
        android:textColor="#800080"
        android:textSize="16sp"
        app:layout_constraintTop_toBottomOf="@id/buttonLogin"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
</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="#36342f">

    <!-- Закрепленный заголовок сверху -->
    <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="@color/button_normal"
        android:textColor="@color/text_color_primary"
        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="@color/button_normal"
        android:textColor="@color/text_color_primary"
        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="@color/button_normal"
        android:textColor="@color/text_color_primary"
        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="@color/button_normal"
        android:textColor="@color/text_color_primary"
        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"/>

    <ImageButton
        android:id="@+id/button_help"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:background="?attr/selectableItemBackgroundBorderless"
        android:src="@drawable/ic_help"
        android:contentDescription="Помощь"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:scaleType="centerInside"
        android:layout_marginStart="8dp"/>/>
</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:background="#36342f"
    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="#36342f"
    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:
<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="#36342f">

    <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"/>

    <!-- Заголовок для статистики -->
    <TextView
        android:id="@+id/textViewStatsTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Статистика игр"
        android:textSize="20sp"
        android:textStyle="bold"
        android:textColor="#FFA500"
        app:layout_constraintTop_toBottomOf="@id/textViewEmail"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="24dp"/>

    <!-- Статистика игры "Мастер памяти" -->
    <TextView
        android:id="@+id/textViewMemoryGameStats"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Мастер памяти:\nЛучший счет: \nЛучшее время: "
        android:textSize="16sp"
        android:textColor="#800080"
        app:layout_constraintTop_toBottomOf="@id/textViewStatsTitle"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="16dp"/>

    <!-- Статистика игры "Игра на реакцию" -->
    <TextView
        android:id="@+id/textViewReactionGameStats"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Игра на реакцию:\nЛучшее время реакции: "
        android:textSize="16sp"
        android:textColor="#800080"
        app:layout_constraintTop_toBottomOf="@id/textViewMemoryGameStats"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="16dp"/>

    <!-- Статистика игры "Найди пару плиток" -->
    <TextView
        android:id="@+id/textViewPairMatchingGameStats"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Найди пару плиток:\nЛучшие ходов: \nБыстрейшее время: "
        android:textSize="16sp"
        android:textColor="#800080"
        app:layout_constraintTop_toBottomOf="@id/textViewReactionGameStats"
        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/textViewPairMatchingGameStats"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="24dp"/>



</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="#36342f"
    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="@color/button_normal"
        android:textColor="@color/text_color_primary"
        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="@color/button_normal"
        android:textColor="@color/text_color_primary"
        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:
<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/reactionModeLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#36342f"
    android:padding="16dp">

    <!-- Заголовок -->
    <TextView
        android:id="@+id/select_mode_label"
        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" />

    <!-- Кнопка "Свободный режим" -->
    <FrameLayout
        android:id="@+id/btn_free_mode_container"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/select_mode_label"
        app:layout_constraintBottom_toTopOf="@id/btn_level_mode_container"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="200dp">

        <Button
            android:id="@+id/btn_free_mode"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Свободный режим"
            android:background="@color/button_normal"
            android:textColor="@color/text_color_primary"/>

        <ImageView
            android:id="@+id/lock_free_mode"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/ic_lock"
            android:layout_gravity="center|end"
            android:layout_marginEnd="8dp"
            android:visibility="gone"/>
    </FrameLayout>

    <!-- Wrap btn_level_mode in a FrameLayout too (similar to btn_free_mode) -->
    <FrameLayout
        android:id="@+id/btn_level_mode_container"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/btn_free_mode_container"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp">

        <Button
            android:id="@+id/btn_level_mode"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Режим уровней"
            android:background="@color/button_normal"
            android:textColor="@color/text_color_primary"/>
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>







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:id="@+id/registerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#36342f"
    android:padding="16dp">

    <!-- Заголовок -->
    <TextView
        android:id="@+id/register_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_constraintBottom_toTopOf="@id/editTextNickname"
        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_constraintBottom_toTopOf="@id/editTextEmail"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Поле ввода Email -->
    <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_constraintBottom_toTopOf="@id/editTextPassword"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Поле ввода пароля -->
    <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_constraintBottom_toTopOf="@id/buttonRegister"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Кнопка "Зарегистрироваться" -->
    <Button
        android:id="@+id/buttonRegister"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="Зарегистрироваться"
        android:background="@color/button_normal"
        android:textColor="@color/text_color_primary"
        app:layout_constraintTop_toBottomOf="@id/editTextPassword"
        app:layout_constraintBottom_toTopOf="@id/textViewLoginLink"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Ссылка на вход -->
    <TextView
        android:id="@+id/textViewLoginLink"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Уже есть аккаунт? Войти"
        android:textColor="#800080"
        android:textSize="16sp"
        app:layout_constraintTop_toBottomOf="@id/buttonRegister"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
</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:id="@+id/tileSelectionLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#36342f"
    android:padding="16dp">

    <!-- Заголовок -->
    <TextView
        android:id="@+id/select_tile_label"
        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" />

    <!-- Кнопка "1 Плитка" -->
    <FrameLayout
        android:id="@+id/btn_one_tile_container"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/select_tile_label"
        app:layout_constraintBottom_toTopOf="@id/btn_two_tiles_container"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="200dp">

        <Button
            android:id="@+id/btn_one_tile"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="1 Плитка"
            android:background="@color/button_normal"
            android:textColor="@color/text_color_primary"/>

        <ImageView
            android:id="@+id/lock_one_tile"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/ic_lock"
            android:layout_gravity="center|end"
            android:layout_marginEnd="8dp"
            android:visibility="gone"/>
    </FrameLayout>

    <!-- Кнопка "2 Плитки" -->
    <FrameLayout
        android:id="@+id/btn_two_tiles_container"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/btn_one_tile_container"
        app:layout_constraintBottom_toTopOf="@id/btn_four_tiles_container"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp">

        <Button
            android:id="@+id/btn_two_tiles"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="2 Плитки"
            android:background="@color/button_normal"
            android:textColor="@color/text_color_primary"/>

        <ImageView
            android:id="@+id/lock_two_tiles"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/ic_lock"
            android:layout_gravity="center|end"
            android:layout_marginEnd="8dp"
            android:visibility="gone"/>
    </FrameLayout>

    <!-- Кнопка "4 Плитки" -->
    <FrameLayout
        android:id="@+id/btn_four_tiles_container"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/btn_two_tiles_container"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp">

        <Button
            android:id="@+id/btn_four_tiles"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="4 Плитки"
            android:background="@color/button_normal"
            android:textColor="@color/text_color_primary"/>

        <ImageView
            android:id="@+id/lock_four_tiles"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/ic_lock"
            android:layout_gravity="center|end"
            android:layout_marginEnd="8dp"
            android:visibility="gone"/>
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>




dialog_help.xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:background="@color/transparent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <!-- Пример для игры "Мастер памяти" -->
        <TextView
            android:id="@+id/classicInfoTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Мастер памяти"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="#FFA500"
            android:paddingBottom="8dp"/>

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:src="@drawable/grid2x2"
            android:scaleType="centerInside"
            android:contentDescription="Игра Мастер памяти"/>

        <TextView
            android:id="@+id/classicInfoDesc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/MemoryGame"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:paddingBottom="16dp"/>

        <!-- Повторите блок для других игр -->
        <!-- Пример для "Игра на реакцию" -->
        <TextView
            android:id="@+id/timedInfoTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Игра на реакцию"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="#FFA500"
            android:paddingBottom="8dp"/>

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:src="@drawable/stopwatch"
            android:scaleType="centerInside"
            android:contentDescription="Игра на реакцию"/>

        <TextView
            android:id="@+id/timedInfoDesc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/Reaction"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:paddingBottom="16dp"/>

        <!-- Пример для "Найди пару плиток" -->
        <TextView
            android:id="@+id/reflexInfoTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Найди пару плиток"
            android:textSize="20sp"
            android:textStyle="bold"
            android:textColor="#FFA500"
            android:paddingBottom="8dp"/>

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:src="@drawable/fast_hand"
            android:scaleType="centerInside"
            android:contentDescription="Найди пару плиток"/>

        <TextView
            android:id="@+id/reflexInfoDesc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/reflexExplanation"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:paddingBottom="16dp"/>

        <!-- Кнопка закрытия окна помощи -->
        <Button
            android:id="@+id/btn_close_help"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Закрыть"
            android:textColor="#FFFFFF"
            android:background="@drawable/rectangle_medium"
            android:padding="12dp"
            android:textSize="16sp"/>

    </LinearLayout>
</ScrollView>






// 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")
}