Игра на реакцию (Reaction Game)
Игра на реакцию проверяет, насколько быстро игрок может реагировать на визуальные стимулы (например, подсвеченные плитки). В этой игре предусмотрены два основных режима: Свободный режим и Режим уровня.
Виды игр:
1. Свободный режим (Free Mode):
   * Цель: Игроку нужно как можно быстрее нажать на плитку, которая случайным образом подсвечивается на игровом поле.
   * Прогресс игры:
      * После нажатия кнопки «Старт» начинается обратный отсчёт.
      * Плитка подсвечивается случайным образом после завершения обратного отсчёта, и игрок должен нажать на неё как можно быстрее.
   * Оценка реакции: После каждого нажатия игроку показывается время его реакции, и в зависимости от скорости, игра выдает оценку:
      * Отличная реакция (≤ 200 мс)
      * Хорошая реакция (≤ 300 мс)
      * Средняя реакция (≤ 500 мс)
      * Можно быстрее (> 500 мс)
   * Конец игры: Игра завершается после одного раунда, и игрок может начать новый раунд с новыми результатами.
2. Режим уровня (Level Mode):
   * Цель: Игрок должен пройти несколько уровней, на каждом из которых его задача — реагировать быстрее установленного времени для данного уровня.
   * Прогресс уровней:
      * Игра начинается с первого уровня, и каждый уровень имеет своё целевое время реакции (например, 1000 мс, 900 мс, 800 мс и т.д.).
      * Если реакция игрока быстрее целевого времени для уровня, он продвигается на следующий уровень.
      * На более высоких уровнях целевое время реакции сокращается, делая игру всё сложнее.
   * Завершение уровня: Когда игрок успешно завершает уровень, на экране появляется кнопка «Следующий уровень». Если игрок не успевает уложиться в отведённое время, ему предлагается начать игру с первого уровня или вернуться в главное меню.
   * Завершение игры: Когда игрок проходит все уровни, игра завершает с победой, предлагая начать заново или вернуться в главное меню.
Дополнительные возможности:
* Сохранение результатов: Для каждого режима игры на реакцию сохраняются лучшие результаты игрока.
* Плитки разного количества: Игрок может выбрать количество плиток (1, 2, 4 плитки), что влияет на сложность игры.
это описание моей игры
и мой код по моему там не полностью соответствует моей задумке 


package com.example.memorygameone;

import android.app.Activity;
import android.content.SharedPreferences;
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.graphics.Color;
import android.widget.Toast;

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

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

public class ReactionGame extends Activity {

    private GridLayout gridLayout;
    private ArrayList<Button> squares;
    private TextView title, reactionTimeText, countdownText;
    private Button startButton, retryButton;
    private Handler handler;
    private Stopwatch stopwatch;
    private int reactionTime;
    private DB dbHelper;
    private SharedPreferences preferences;
    private String playerNickname;
    private int tileCount;
    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 enum GameState {
        IDLE,
        COUNTDOWN,
        SHOW_SQUARE,
        WAITING_FOR_REACTION,
        REACTION_COMPLETE
    }

    private GameState gameState = GameState.IDLE;

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

        initializeViews();
        initializeGame();

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

        setupListeners();
    }

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

    private void initializeGame() {
        tileCount = getIntent().getIntExtra("tileCount", 3); // Установлено значение по умолчанию 3, при необходимости измените
        dbHelper = new DB(this);
        preferences = getSharedPreferences("game_prefs", MODE_PRIVATE);
        playerNickname = preferences.getString("nickname", "Player");
        stopwatch = new Stopwatch();
        handler = new Handler();
        soundManager = SoundManager.getInstance(this);

        setupGameGrid();
    }

    private void startLevelMode() {
        updateLevelTitle();
    }

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

    private void setupListeners() {
        // Единичный слушатель для startButton
        startButton.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            if ("level".equals(gameMode)) {
                if (currentLevel > levelTargets.length) {
                    // Игра завершена
                    gameCompleted();
                } else {
                    // Запуск уровня
                    startCountdown();
                }
            } else {
                // Свободный режим
                startCountdown();
            }
        });

        // Слушатель для retryButton
        retryButton.setOnClickListener(v -> {
            soundManager.playButtonClickSound();
            startCountdown();
            retryButton.setVisibility(View.GONE);
        });
    }

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

        int columnCount = calculateColumnCount(tileCount);
        gridLayout.setColumnCount(columnCount);

        int screenWidth = getResources().getDisplayMetrics().widthPixels;
        int tileSize = (screenWidth - 60) / columnCount; // 60 - общий отступ (по 30 с каждой стороны)

        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.setClickable(false); // Изначально плитки не кликабельны
            button.setOnClickListener(this::checkReaction);
            squares.add(button);
            gridLayout.addView(button);
        }
    }

    private int calculateColumnCount(int tileCount) {
        if (tileCount == 2) {
            return 2;
        }
        // Найти делитель, ближайший к квадратному корню
        int sqrt = (int) Math.sqrt(tileCount);
        for (int i = sqrt; i >= 1; i--) {
            if (tileCount % i == 0) {
                return i;
            }
        }
        // Если делитель не найден, установить 3 столбца по умолчанию
        return 3;
    }

    private void startCountdown() {
        // Установить состояние COUNTDOWN
        gameState = GameState.COUNTDOWN;

        // Скрыть кнопки старт и повтор и подготовить UI
        startButton.setVisibility(View.GONE);
        retryButton.setVisibility(View.GONE);
        countdownText.setVisibility(View.VISIBLE);
        reactionTimeText.setText("");
        title.setText("Готовьтесь...");

        // Отключить кликабельность плиток во время обратного отсчёта
        setAllSquaresClickable(false);

        // Начать обратный отсчёт
        countdownText.setText("3");
        handler.postDelayed(() -> countdownText.setText("2"), 1000);
        handler.postDelayed(() -> countdownText.setText("1"), 2000);
        handler.postDelayed(() -> {
            countdownText.setVisibility(View.GONE);
            gameState = GameState.SHOW_SQUARE;
            startReactionGame();
        }, 3000);
    }

    private void startReactionGame() {
        title.setText("Ждите...");

        int randomDelay = new Random().nextInt(4000) + 1000; // Задержка от 1000 до 5000 мс

        showSquareRunnable = () -> {
            showRandomSquare();
            stopwatch.start();
            gameState = GameState.WAITING_FOR_REACTION;
            // Включить кликабельность плиток
            setAllSquaresClickable(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 (gameState != GameState.WAITING_FOR_REACTION) {
            // Игнорировать клики вне ожидания реакции
            return;
        }

        Button clickedButton = (Button) view;

        // Отключить остальные плитки после первого клика
        setAllSquaresClickable(false);

        // Проверка на неправильный выбор плитки
        if (clickedButton != correctSquare) {
            title.setText("Ошибка! Нажата неверная плитка.");
            reactionTimeText.setText("Время реакции: --- мс");
            resetGame();
            return;
        }

        // Корректный выбор плитки
        stopwatch.stop();
        reactionTime = (int) stopwatch.getElapsedTimeMillis();
        clickedButton.setBackgroundColor(Color.BLUE);
        reactionTimeText.setText("Время реакции: " + reactionTime + " мс");

        if ("level".equals(gameMode)) {
            checkLevelCompletion(reactionTime);
        } else {
            evaluateReactionTime(reactionTime);
            resetGame(); // В свободном режиме сбрасываем сразу
        }

        saveBestTime();
    }

    private void checkLevelCompletion(int reactionTime) {
        if (reactionTime <= levelTargets[currentLevel - 1]) {
            // Успешно прошёл уровень
            if (currentLevel >= levelTargets.length) {
                gameCompleted();
            } else {
                levelCompleted();
            }
        } else {
            // Неуспешно прошёл уровень
            title.setText("Попробуйте еще раз. Цель: " + levelTargets[currentLevel - 1] + " мс");
            reactionTimeText.setText("Время реакции: " + reactionTime + " мс");
            // Показать кнопки для повторного прохождения или возврата в главное меню
            retryButton.setVisibility(View.VISIBLE);
        }
    }

    private void levelCompleted() {
        soundManager.playLevelUpSound();
        title.setText("Уровень " + currentLevel + " пройден!");
        startButton.setText("Следующий уровень");
        startButton.setVisibility(View.VISIBLE);
        // Инкрементировать уровень для следующего запуска
        currentLevel++;
    }

    private void gameCompleted() {
        soundManager.playVictorySound();
        showConfetti();
        title.setText("Поздравляем! Вы прошли все уровни!");
        startButton.setText("Играть снова");
        startButton.setVisibility(View.VISIBLE);
        // Сброс уровня для повторной игры
        currentLevel = 1;
    }

    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 saveBestTime() {
        boolean newRecord;
            newRecord = dbHelper.updateReactionGameStats(playerNickname, reactionTime);
        if (newRecord) {
            soundManager.playNewRecordSound();
            showConfetti();
            String currentTitle = title.getText().toString();
            title.setText(currentTitle + " Новый рекорд!");
        }
    }

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

        for (Button square : squares) {
            square.setBackgroundColor(Color.WHITE);
        }

        gameState = GameState.IDLE;

        // Сброс переменных и UI
        countdownText.setVisibility(View.GONE);
        reactionTimeText.setText("");
        correctSquare = null;

        // Отключить кликабельность плиток
        setAllSquaresClickable(false);

        // Показать кнопку старт, если это не LevelMode или уровень не завершён
        if (!"level".equals(gameMode) || (currentLevel <= levelTargets.length)) {
            startButton.setVisibility(View.VISIBLE);
        }

        // Скрыть кнопку повторного запуска
        retryButton.setVisibility(View.GONE);

        // Сброс заголовка для свободного режима
        if (!"level".equals(gameMode)) {
            title.setText("Свободный режим: Проверьте вашу реакцию");
        }
    }

    private void setAllSquaresClickable(boolean clickable) {
        for (Button square : squares) {
            square.setClickable(clickable);
        }
    }

    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() {
        if (currentLevel <= levelTargets.length) {
            title.setText("Уровень " + currentLevel + ": Реагируйте быстрее чем " + levelTargets[currentLevel - 1] + " мс");
        }
    }
}