📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados na Especialização em Android Nativo. Recomendado revisar antes de iniciar.

🎮 P03: Genius / Simon Says Clássico em Kotlin

v1.0 — Android Nativo, Kotlin, Sons em Baixa Latência, Temporização com Coroutines e Feedback Visual

Série Prática Android Clássico — Projeto 3 de 21


🏛️ Visão Geral & Arquitetura Visual

O Genius / Simon Says Clássico reproduz o icônico jogo eletrônico de memória dos anos 80. O projeto explora tópicos avançados de desenvolvimento mobile: emissão de tons sonoros sintetizados em baixa latência com SoundPool, reprodução sequencial de luzes através de Coroutines com delay cooperativo, e validação em tempo real das entradas do usuário com controle de rodadas e recorde.

Arquitetura do Jogo Genius Clássico
Tela do Jogo Genius em Execução

📁 Estrutura de Pastas Anti-Colisão

Todos os artefatos Kotlin, efeitos sonoros em .ogg e telas XML estão organizados na subpasta canônica mobile-android-kotlin/:

catalogo_projetos/android_p03_genius_classic/
├── assets/
│   ├── arquitetura_genius_classic.svg
│   ├── screenshot_p03.png
│   ├── screenshot_genius_classic.png
│   ├── screenshot_genius_final.png
│   ├── screenshot_genius_pro.png
│   └── screenshot_genius_ultra.png
├── mobile-android-kotlin/
│   ├── app/
│   │   ├── src/
│   │   │   └── main/
│   │   │       ├── java/br/com/curso/genius/
│   │   │       │   └── MainActivity.kt
│   │   │       ├── res/
│   │   │       │   ├── raw/
│   │   │       │   │   ├── som_verde.ogg
│   │   │       │   │   ├── som_vermelho.ogg
│   │   │       │   │   ├── som_amarelo.ogg
│   │   │       │   │   └── som_azul.ogg
│   │   │       │   └── layout/activity_main.xml
│   │   │       └── AndroidManifest.xml
│   │   └── build.gradle
│   ├── gradle/
│   ├── build.gradle
│   ├── gradlew
│   ├── settings.gradle
│   └── README.md
└── index.md

🧑‍💼 Engenharia de Software & Especificação Formal

1. Histórias de Usuário (User Stories)

ID Persona Narrativa da Funcionalidade Critério de Aceitação Principal Prioridade
US-01 Jogador Quero observar o app piscar uma sequência luminosa acompanhada de som para memorizar a ordem. Execução sequencial com delay ajustável por nível de dificuldade. Alta
US-02 Jogador Quero repetir a sequência tocando nos 4 botões coloridos com som e brilho imediatos. Resposta de áudio com latência inferior a 15ms via SoundPool. Alta
US-03 Jogador Quero avançar para uma rodada mais longa e rápida sempre que acertar toda a sequência. Adição de mais uma cor e incremento do placar de rodadas. Alta
US-04 Jogador Quero receber som característico de erro e ver minha pontuação final caso cometa um deslize. Interrupção imediata da rodada e exibição do diálogo de Game Over. Média
US-05 Desenvolvedor Quero pausar ou liberar os recursos de áudio no ciclo onPause/onDestroy para não gastar bateria. Desalocação correta dos streams de áudio do SoundPool. Baixa

2. Requisitos do Sistema

Requisitos Funcionais (RF)

Requisitos Não-Funcionais (RNF)


3. Diagrama de Casos de Uso

flowchart TD
    Player([fa:fa-user Jogador])
    Engine[(fa:fa-music SoundPool & Coroutines)]

    subgraph AppGenius ["🎮 Jogo Genius (mobile-android-kotlin)"]
        UC01["Iniciar Nova Rodada"]
        UC02["Emitir Sequência Luz e Som"]
        UC03["Receber Toques do Jogador"]
        UC04["Validar Correspondência de Sequência"]
        UC05["Finalizar Partida / Gravar Recorde"]
    end

    Player --> UC01
    UC01 --> UC02
    UC02 --> Engine
    Player --> UC03
    UC03 --> UC04
    UC04 -->|Erro| UC05

4. Matriz de Rastreabilidade (RTM)

ID Requisito História de Usuário Artefato de Código Teste de Aceitação
RF-01 US-01, US-02 mobile-android-kotlin/app/src/main/res/layout/activity_main.xml testButtonsColorState()
RF-02 US-03 mobile-android-kotlin/app/.../MainActivity.kt testSequenceExpandsEachRound()
RF-03 US-01 mobile-android-kotlin/app/.../MainActivity.kt testPlaybackCoroutineDelays()
RF-04 US-02, US-04 mobile-android-kotlin/app/.../MainActivity.kt testPlayerInputComparison()
RF-05 US-04 mobile-android-kotlin/app/.../MainActivity.kt testHighScorePersistence()

🛠️ Implementação Guiada (MainActivity.kt)

📄 mobile-android-kotlin/app/src/main/java/br/com/curso/genius/MainActivity.kt (arquivo real do projeto)

package br.com.curso.genius

import android.content.Context
import android.media.MediaPlayer
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import br.com.curso.genius.databinding.ActivityMainBinding
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private val sequenciaComputador = mutableListOf<Int>()
    private var passoJogador = 0
    private var score = 0
    private var highScore = 0
    private var jogoAtivo = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        highScore = getPreferences(Context.MODE_PRIVATE).getInt("HIGH_SCORE", 0)
        atualizarPlacar()

        configurarBotoes()
        binding.btnStart.setOnClickListener { iniciarJogo() }
    }

    private fun configurarBotoes() {
        val botoes = listOf(binding.btnVerde, binding.btnVermelho, binding.btnAmarelo, binding.btnAzul)
        botoes.forEachIndexed { index, button ->
            button.setOnClickListener { 
                if (jogoAtivo) aoClicar(index) 
            }
            button.isEnabled = false
        }
    }

    private fun iniciarJogo() {
        sequenciaComputador.clear()
        score = 0
        binding.btnStart.visibility = View.GONE
        binding.txtStatus.text = "PREPARE-SE!"
        iniciarNovaRodada()
    }

    private fun iniciarNovaRodada() {
        passoJogador = 0
        score = sequenciaComputador.size
        atualizarPlacar()
        
        sequenciaComputador.add((0..3).random())
        
        lifecycleScope.launch {
            tocarSequencia()
        }
    }

    private suspend fun tocarSequencia() {
        jogoAtivo = false
        binding.txtStatus.text = "OBSERVE..."
        delay(1000)

// … (+89 linhas: veja o arquivo completo no projeto)

🚀 Como Executar no Laboratório

1. Navegue até a subpasta do projeto

cd catalogo_projetos/android_p03_genius_classic/mobile-android-kotlin

2. Compile e execute no emulador ou dispositivo com áudio habilitado

./gradlew assembleDebug
./gradlew test

🏁 Matriz de Checkpoints de Conclusão

Checkpoint Meta Técnica Critério de Aceite Status
CP-01 Configuração do SoundPool 4 sons carregados sem exceção via onLoadCompleteListener PASS ✅
CP-02 Sequenciador com Coroutines Delay não-bloqueante reproduzindo piscar e tom sonoro PASS ✅
CP-03 Validação Passo a Passo Algoritmo detecta erro imediatamente no primeiro clique errado PASS ✅
CP-04 Recorde Local Pontuação máxima mantida salva entre execuções do app PASS ✅
CP-05 Liberação de Memória soundPool.release() executado no encerramento da Activity PASS ✅