PlayerPuzzle

Atividade gamificada Match-3 RPG por turnos para o Moodle — documentação completa.

English | Português

View on GitHub Download .zip

Moodle Licença Status Última Versão PlayerGames Ecosystem Autor

Moodle Plugin CI Último Commit Issues Abertas

⚠️ Este plugin está em desenvolvimento ativo. Ainda não foi publicado no Diretório de Plugins do Moodle. Algumas funcionalidades descritas abaixo são planejadas e ainda não estão implementadas — veja Funcionalidades pra saber exatamente quais são quais.

O PlayerPuzzle (mod_playerpuzzle) é uma atividade gamificada Match-3 RPG por turnos para o Moodle: o estudante combina peças num tabuleiro 8×8 para atacar um chefe controlado por uma IA simples, respondendo questões puxadas do próprio Banco de Questões do Moodle no curso pra desferir ataques críticos. O professor escolhe entre uma Partida Única autocontida ou uma Campanha de até 10 níveis (10 fases cada); a progressão permanente opcional (moedas, nível de espada/escudo) é resolvida inteiramente pelo block_playerhud companheiro, quando presente no curso — o PlayerPuzzle não mantém economia própria.

👈 Use a barra lateral para ir direto a qualquer seção desta página.


🖼️ Capturas de Tela

Navegue com as setas, os pontos, ou as teclas esquerda/direita. Clique na imagem para ampliar.

✨ Funcionalidades

✅ Implementado

⏳ Em Desenvolvimento / Planejado

O plugin é um software em estágio Alpha: tudo em "Implementado" acima funciona hoje e é coberto pela suíte de testes automatizados; tudo em "Planejado" está desenhado (ver o roteiro interno do projeto), mas ainda não construído.

⚔️ Como o Combate Funciona

O combate roda num tabuleiro Match-3 8×8 (Phaser 3), priorizando retrato (540×960), se adaptando a um layout paisagem em telas mais largas.

Estrutura de Turnos

  1. O estudante faz uma jogada no tabuleiro.
  2. Combos e cascatas mantêm o turno do estudante rolando.
  3. Quando o tabuleiro estabiliza, é o Turno do Chefe: uma IA simples varre o tabuleiro e executa uma troca válida. O que ela combina resolve pelas mesmas sete peças e os mesmos efeitos do turno do estudante (veja abaixo) — o chefe pode causar dano, acumular multiplicador, envenenar o estudante, se proteger, se curar e acumular moeda, exatamente como o estudante faz no próprio turno.

As Sete Peças

Efeitos simétricos: cada peça faz a mesma coisa não importa de qual lado está jogando — o que muda é só quem ganha o efeito (quem estiver no turno atual).

Peça Efeito
⭐ Estrela Soma +0,1 a um multiplicador de dano, acumulando entre turnos; consumido no acerto crítico daquele lado e resetado a 1 quando esse lado erra uma pergunta
📖 Grimório Enche um medidor de veneno próprio de 0–100 (+10 por peça); ao chegar em 100, arma 3 rodadas de dano contra o oponente (uma por turno dele) e reseta, preservando o excedente
❓ Orbe de Interrogação Enche uma barra de energia de 100 pontos própria de cada lado; ao chegar em 100, pausa o combate e abre o Desafio de Pergunta — o estudante responde de verdade, o chefe “responde” escolhendo uma opção ao acaso, validada do mesmo jeito no servidor
⚔️ Espadas Dano direto no oponente — sem precisar de pergunta. Escala com nível/fase, e uma combinação de 4 ou 5 peças causa 1,5x/2x o dano de uma combinação de 3
🛡️ Escudo Enche um medidor próprio de 0–100 (+10 por peça); ao chegar em 100, nega por completo o próximo golpe recebido por esse lado, não importa o valor, e reseta
🧪 Poção Cura o HP do próprio lado — escala do mesmo jeito que o dano de Espada, na metade da taxa de uma combinação de Espada do mesmo tamanho
🪙 Moeda Soma ao contador de ouro próprio da partida — veja a fórmula por combinação e a compensação líquida em Integração com o PlayerHUD

O Desafio de Pergunta

Quando a barra de Orbe de Interrogação de qualquer um dos lados chega a 100%, a partida pausa e abre o modal de pergunta:

É o momento em que o plugin conecta a habilidade no Match-3 com o conhecimento real de conteúdo: combinar peças bem enche a barra e acumula o multiplicador, mas o retorno de verdade só vem se o estudante também responder certo.

Verdade do Servidor

Todo número que governa o combate — o HP máximo do chefe e do estudante, e o valor base de dano do qual Espada/Estrela/Grimório/Poção escalam — é calculado uma única vez no servidor pro nível e fase atuais (veja Modos de Jogo pra como esses valores escalam) e entregue ao cliente como configuração; o cliente nunca recalcula nada disso. O dano que o cliente reporta de volta é limitado no servidor contra esse mesmo HP do chefe escalado pela fase antes de ser aceito — veja Segurança e Anti-Trapaça.

🎯 Motor de Questões

Fonte

O PlayerPuzzle lê as questões do próprio Banco de Questões do Moodle (core_question) — o professor escolhe uma categoria já existente nas configurações da atividade, e qualquer questão de Múltipla Escolha ou Verdadeiro/Falso nela pode ser usada durante o combate. Isso significa que um professor que já mantém um banco de questões pro Quiz pode apontar o PlayerPuzzle pra mesma categoria e reaproveitá-la na hora, sem migrar conteúdo nenhum.

Uma segunda fonte de questões, própria do PlayerPuzzle (cadastro manual mais geração assistida por IA), está planejada pra coexistir ao lado do Banco de Questões real — veja Funcionalidades.

JSON Cego — a Resposta Nunca Vaza

O texto da pergunta e todas as opções de resposta são enviados ao navegador, mas o id da resposta correta não é. O question_fetcher.php remove esse dado no servidor antes da pergunta sequer chegar ao cliente. Quando o estudante envia uma escolha, o mod_playerpuzzle_validate_answer confere contra o registro real no banco de dados e devolve só um booleano mais (numa resposta errada) a opção correta — então nada no código-fonte da página ou no tráfego de rede revela a resposta com antecedência. Esse é o primeiro dos três pilares anti-trapaça do plugin.

Mínimo de Questões por Partida

As configurações da atividade permitem que o professor exija um número mínimo de perguntas respondidas antes que uma partida possa terminar em vitória, e o Lobby mostra essa exigência ao estudante antes de ele começar. A regra de jogo que aplica isso de fato — reviver o chefe com metade do HP se o mínimo ainda não foi atingido — ainda não está implementada; veja Funcionalidades.

🎮 Modos de Jogo

O professor escolhe um modo por instância da atividade.

Campanha

Uma jornada contínua por até 10 níveis, cada um dividido em 10 fases. Vencer uma fase nunca consome uma nova tentativa — só perder (ou o tempo esgotar) consome, e o limite configurado de Máximo de Tentativas só conta tentativas finalizadas (perdidas/tempo esgotado/abandonadas), nunca uma fase vencida ao longo do caminho.

Partida Única

Uma única rodada autocontida — sem níveis ou fases. A nota reaproveita o padrão grademethod já estabelecido pelas atividades irmãs mod_playerwords/mod_playercross: o professor define um limite de Máximo de Partidas (ou deixa ilimitado) por um seletor 0–10, na mesma escala de uma rodada curta e repetível, em vez de uma campanha longa.

Por Que Dois Modos em Vez de Um Só

Uma “tentativa” de Campanha e uma “tentativa” de Partida Única são estruturalmente diferentes — uma jornada contínua atravessando várias fases contra rodadas independentes e repetíveis — então o plugin modela os dois separadamente em vez de forçar um único esquema de nota/contagem de tentativas caber nos dois casos. question_fetcher.php, save_progress.php e o Lobby leem o gamemode configurado pra saber quais regras se aplicam.

💰 Integração com o PlayerHUD

O PlayerPuzzle não tem nenhuma economia própria permanente. Moedas e níveis de upgrade vivem inteiramente no block_playerhud, e o PlayerPuzzle só lê/escreve nele quando o bloco está presente no curso — o plugin funciona sozinho, sem nenhuma configuração, se ele não estiver.

Itens Escolhidos pelo Professor, Nunca Auto-Detectados

Pra cada curso, o professor escolhe explicitamente quais itens do próprio bloco representam:

Cada um desses é um simples dropdown dos itens já configurados no bloco — o mesmo padrão já usado pelas atividades irmãs mod_playerwords/mod_playercross — então adicionar essa integração nunca exigiu nenhuma mudança no block_playerhud em si.

Ganho de Moeda e a Compensação do Chefe

Combinar a peça Moeda (veja Como o Combate Funciona) rende moedas numa curva de combo: uma combinação de 3 rende o valor base configurado em “Ganho de Moeda”, uma de 4 rende 1,5x esse valor, e uma de 5 rende 2x — a mesma curva que o dano da Espada usa. Diferente de todo o resto do combate, o Ganho de Moeda deliberadamente não escala por nível ou fase, então o professor pode configurar preços fixos de consumíveis (Poção, Escudo, Magia Rápida, Dica) que continuam equilibrados durante toda a campanha, em vez de ficarem proporcionalmente mais baratos conforme ela avança.

O chefe também acumula sua própria conta de Moeda quando combina a peça no próprio turno — ele não tem loja e nunca gasta esse valor. Seu único propósito é compensar o saldo do estudante ao fim da partida: a contagem final de moedas do estudante é max(0, moedas do estudante − moedas do chefe), creditada via PlayerHUD só em vitória. Isso dá à IA do chefe um motivo pra “competir” pela mesma peça que o estudante quer, em vez de ela ficar inerte sempre que a IA combiná-la.

O Que Já Está Ligado Hoje

Veja Funcionalidades pra a divisão completa entre implementado e planejado.

♿ Acessibilidade

O Desenho

A jogabilidade Match-3 vive num Canvas, que é opaco pra leitores de tela por natureza. A abordagem de acessibilidade do PlayerPuzzle é uma camada HTML paralela, sempre presente, em vez de um complemento pensado depois:

O Que Já Está Implementado Hoje

O Que Está Planejado

O speechSynthesis nunca é usado como canal padrão de anúncio, pra evitar conflito com qualquer leitor de tela que o estudante já esteja usando.

🔐 Segurança e Anti-Trapaça

Todo número de combate que o estudante vê é uma afirmação que o cliente faz sobre si mesmo. O PlayerPuzzle não trata nada disso como confiável até o servidor confirmar, através de três pilares:

1. Servidor Como Fonte da Verdade

O HP do chefe e do estudante pro nível/fase atuais são calculados uma única vez, no servidor (combat::calculate_boss_hp()/calculate_student_hp()), e entregues ao cliente como configuração somente-leitura. O cliente nunca os recalcula.

2. Dano Verificado por Sanidade

Qualquer dano que o cliente reporte ao fim de uma partida é limitado contra o HP real do chefe, já escalado pela fase, antes de ser aceito — nunca o HP base, e nunca o que o cliente afirma que deveria ser o teto. Avançar uma fase (advance_phase) aplica a mesma regra: o servidor exige que o dano reportado tenha realmente zerado o HP do chefe daquela fase antes de permitir a transição.

Uma checagem de sanidade mais completa — considerando multiplicadores de upgrade do PlayerHUD e tempo de jogo decorrido — está desenhada, mas ainda não implementada; veja Funcionalidades.

3. Tokens Anti-Replay de Uso Único

Toda chamada que altera estado (iniciar uma partida, retomar uma tentativa de campanha, avançar uma fase) emite um token novo e invalida o anterior. Uma requisição capturada não pode ser reproduzida pra disparar a mesma recompensa duas vezes — retomar uma campanha no meio da jornada rotaciona o token exatamente como terminar uma faz, então a garantia se mantém mesmo numa sessão que atravessa vários carregamentos de página, não só dentro de uma única partida.

JSON Cego

O id da resposta correta nunca é enviado ao cliente — veja Motor de Questões pra como isso é garantido na camada de dados, não só na interface.

🎓 Finalidade Educacional

O PlayerPuzzle é pensado para:

Adequado para:

🕹️ Ecossistema PlayerGames

O PlayerPuzzle faz parte do ecossistema de gamificação PlayerGames para o Moodle. Nenhum dos plugins abaixo é obrigatório — o PlayerPuzzle funciona sozinho — mas instalar os relevantes pra um curso libera as integrações documentadas ao longo desta página.

📦 Requisitos

Componente Versão
Moodle 4.5 – 5.2
PHP 8.2+

O block_playerhud é uma dependência opcional — veja Integração com o PlayerHUD.

🛠️ Instalação e Configuração

⚠️ Este plugin ainda não está publicado no Diretório de Plugins do Moodle. Instale manualmente a partir deste repositório.

  1. Baixe o arquivo .zip ou clone este repositório.
  2. Extraia na pasta mod/ do seu Moodle.
  3. Renomeie para playerpuzzle (se necessário). Caminho final: seu-moodle/mod/playerpuzzle/
  4. Acesse Administração do site → Notificações para concluir a instalação.
  5. Opcional: instale o block_playerhud se quiser a economia opcional de moedas/upgrades — veja Integração com o PlayerHUD.
  6. Adicione uma atividade PlayerPuzzle a qualquer curso, escolha um Modo de Jogo, uma categoria do Banco de Questões, e (opcionalmente) quais itens do PlayerHUD usar.

📖 Como Usar

Para Professores

  1. Adicione uma atividade PlayerPuzzle a um curso.
  2. Escolha um Modo de Jogo — Campanha ou Partida Única — veja Modos de Jogo.
  3. Escolha a categoria do Banco de Questões de onde a partida vai puxar questões.
  4. Opcionalmente, defina um Mínimo de Questões por Partida e se respostas erradas devem contar contra a nota.
  5. Opcionalmente, configure os itens de Integração com o PlayerHUD, se o block_playerhud estiver instalado no curso.
  6. Defina a Nota Máxima — as duas fórmulas de nota escalam por ela, nunca por um 100 fixo.

Para Estudantes

  1. Abra a atividade para chegar ao Lobby — ele mostra seu progresso atual (modo Campanha), seu saldo no PlayerHUD se configurado, e qualquer exigência de mínimo de questões.
  2. Selecione Jogar para começar (ou retomar) uma partida.
  3. Combine peças para atacar o chefe, encha a barra de energia para disparar um Desafio de Pergunta, e responda certo para um acerto crítico.
  4. No modo Campanha, vencer uma fase nunca consome uma nova tentativa — só perder consome.

🧪 Testes Automatizados

O PlayerPuzzle vem com uma suíte PHPUnit extensa cobrindo o motor de combate, o fluxo de questões, a segurança anti-trapaça, a integração com o PlayerHUD, backup/restore, conclusão de atividade e privacidade. Todo push no CI roda a matriz completa (Moodle 4.5 → 5.3, PHP 8.1 → 8.4, PostgreSQL e MariaDB).

PHPUnit — Testes Unitários e de Integração (tests/)

Arquivo de teste Casos
access_test.php 1
backup_restore_test.php 13
generator_test.php 1
lib_completion_info_test.php 6
lib_crud_test.php 8
lib_grade_item_update_test.php 4
lib_pluginfile_test.php 5
lib_supports_test.php 2
lib_update_grades_test.php 4
mod_form_test.php 11
phaser_loading_test.php 2
uninstall_test.php 2
Subtotal 59

Testes de Conclusão (tests/completion/)

Arquivo de teste Casos
custom_completion_test.php 11
Subtotal 11

Testes de Eventos (tests/event/)

Arquivo de teste Casos
game_completed_test.php 3
game_started_test.php 2
Subtotal 5

Testes de Web Services (tests/external/)

Arquivo de teste Casos
advance_phase_test.php 26
buy_stock_test.php 10
draw_question_test.php 8
generate_questions_test.php 2
get_phase_questionlog_test.php 5
save_combat_state_test.php 14
save_generated_questions_test.php 4
save_progress_test.php 29
set_sound_preference_test.php 5
transfer_hud_coins_test.php 8
use_stock_test.php 15
validate_answer_test.php 19
Subtotal 145

Testes de Formulário (tests/form/)

Arquivo de teste Casos
import_form_test.php 3
question_form_test.php 11
Subtotal 14

Testes de Lógica de Negócio Local (tests/local/)

Arquivo de teste Casos
ai_question_generator_test.php 19
attempt_consumables_test.php 7
attempt_questions_test.php 1
coin_ledger_test.php 4
combat_state_test.php 5
game_page_service_test.php 27
grade_calculator_test.php 21
hud_service_test.php 18
lobby_page_service_test.php 22
move_log_test.php 20
question_bank_sync_test.php 13
question_editor_files_test.php 3
question_list_service_test.php 3
questions_repository_test.php 21
ranking_service_test.php 9
replay_credit_test.php 8
report_service_test.php 17
sound_preferences_test.php 7
user_stock_test.php 10
Subtotal 235

Testes do Motor de Combate (tests/local/engine/)

Arquivo de teste Casos
board_engine_test.php 25
combat_engine_test.php 29
combat_test.php 9
golden_vectors_test.php 5
prng_test.php 5
question_fetcher_test.php 20
replay_test.php 28
security_test.php 43
Subtotal 164

Testes de Privacidade (tests/privacy/)

Arquivo de teste Casos
provider_test.php 36
Subtotal 36
Total Geral 669
vendor/bin/phpunit --testsuite mod_playerpuzzle_testsuite

Cobertura (moodle-coverage, PHPUnit + Xdebug): 86,79% de linhas, 74,03% de métodos (21/47 classes totalmente cobertas).

Detalhamento completo teste a teste e tabela de cobertura →

JavaScript — Testes Unitários (tests/js/engine/)

A matemática determinística de match-3/combate/PRNG, rodada sem cabeça via o test runner nativo do Node (node --test) — sem Jest, sem navegador. Primeiro plugin do ecossistema com uma camada de teste JS própria.

Arquivo de teste Casos
board_rules.test.js 25
combat_rules.test.js 29
prng.test.js 5
Subtotal 59
node --test tests/js/engine/board_rules.test.js tests/js/engine/combat_rules.test.js tests/js/engine/prng.test.js

Behat — Testes de Aceitação

Arquivo de feature Cenários
mod_playerpuzzle_accessibility.feature 4
mod_playerpuzzle_gameplay.feature 4
mod_playerpuzzle_match.feature 3
mod_playerpuzzle_ranking.feature 3
mod_playerpuzzle_settings.feature 1
mod_playerpuzzle_smoke.feature 1
Subtotal 16
php admin/tool/behat/cli/init.php
vendor/bin/behat --tags=@mod_playerpuzzle --profile=chrome

🔎 Divulgação de Serviço de Terceiros

Não aplicável hoje. O PlayerPuzzle não chama nenhum serviço externo atualmente — toda questão vem do próprio Banco de Questões do Moodle (veja Motor de Questões), e nenhuma requisição de rede sai do servidor como parte da jogabilidade.

Planejado

Uma segunda fonte de questões — cadastro manual mais geração assistida por IA opcional através do plugin companheiro local_aihub, recorrendo ao próprio core_ai do Moodle — está desenhada para uma fase futura (veja Funcionalidades). Quando ela existir, esta seção vai divulgar os provedores exatos, o que é transmitido, e qualquer implicação de custo, seguindo o mesmo padrão já documentado pelo local_playergames e pelo próprio local_aihub. Assim como nesses plugins, a geração por IA será totalmente opcional e nunca vai incluir dados de estudante num prompt.

📄 Licença

Este projeto é licenciado sob a GNU General Public License v3 (GPLv3).

Copyright: 2026 Jean Lúcio

Suporte

Encontrou um bug ou tem alguma dúvida? Abra uma issue no rastreador de issues.

Mantenedor

Mantido por Jean Lúcio.