feat: adiciona atividade Padrões de reconhecimento de padrões

Nova atividade em app/src/atividades/programacao/padroes/, seguindo o padrão arquitetural das demais (BaseGameScene + Blockly + interpretador):

- Cena Phaser (PadroesScene), blocos customizados, API do interpretador, validador e UI com painéis ENTRADA/SAÍDA.
- Fase 1 (teste): loop "enquanto" que verifica se a ENTRADA contém apenas letras de A-Z (caso "ABC123" -> "INVÁLIDO").
- Registro em gameRegistry.js, rota em App.jsx e ajuste do teste do registry (EXPECTED_IDS -> 10 jogos).
This commit is contained in:
2026-06-27 18:30:53 -03:00
parent fd45878b06
commit c66bb6a9a8
11 changed files with 867 additions and 2 deletions

View File

@@ -0,0 +1,249 @@
/**
* @fileoverview Blocos, geradores e toolbox do jogo Padrões.
*
* Blocos customizados expõem: leitura da ENTRADA, definição/leitura de
* SAÍDA, CONTADOR e LETRA, a constante ALFABETO A-Z e as operações de
* string text_charAt / text_indexOf (0-based, alinhadas ao cripto).
*
* @module games.padroes.blocks.blocks
*/
"use strict";
import * as Blockly from "blockly/core";
import "blockly/blocks";
import { javascriptGenerator } from "blockly/javascript";
import { CORES_BLOCKLY } from "@/blockly/blocklyColors";
import {
configurarGerador,
gerarExpressao,
gerarStatementComValor,
} from "@/blockly/generator";
import { gerarToolboxDeEstrutura } from "@/blockly/toolbox";
import {
criarBlocoExpressaoSimples,
criarBlocoStatementComValor,
} from "@/blockly/blockFactory";
const C = CORES_BLOCKLY; // LOGICA:210, LOOPS:120, MATEMATICA:230, TEXTO:160, VARIAVEIS:330
// Estrutura declarativa da toolbox (cada categoria filtra por allowedBlocks).
const ESTRUTURA_TOOLBOX = [
{
nome: "Entrada/Saída",
blocos: ["obter_entrada", "definir_saida", "obter_saida"],
},
{
nome: "Variáveis",
blocos: [
"definir_contador",
"obter_contador",
"definir_letra",
"obter_letra",
],
},
{ nome: "Repetição", blocos: ["controls_whileUntil"] },
{
nome: "Lógica",
blocos: ["controls_if", "logic_compare", "logic_operation"],
},
{
nome: "Texto",
blocos: ["text", "text_charAt", "text_indexOf", "text_length", "alfabeto"],
},
{ nome: "Matemática", blocos: ["math_number", "math_arithmetic"] },
];
/**
* Registra todos os blocos e geradores do jogo Padrões no Blockly.
* Deve ser chamado uma vez na montagem do componente.
* @returns {void}
*/
export const registerBlocks = () => {
defineBlocks();
defineGenerators();
};
/**
* Gera a toolbox contendo apenas os blocos permitidos pela fase.
* @param {Array<string>} [allowedBlocks=[]] - Lista de blocos habilitados
* @returns {Object} Estrutura `categoryToolbox` para o Blockly
*/
export const generateDynamicToolbox = (allowedBlocks = []) => {
return gerarToolboxDeEstrutura(ESTRUTURA_TOOLBOX, allowedBlocks);
};
// ===================== Definições de blocos =====================
const defineBlocks = () => {
// Entrada (somente leitura — é pré-definida pela fase)
criarBlocoExpressaoSimples(
"obter_entrada",
"ENTRADA",
null,
C.VARIAVEIS,
"O texto que a fase quer analisar",
);
// Saída
criarBlocoStatementComValor(
"definir_saida",
"definir SAÍDA como",
"VALUE",
null,
C.VARIAVEIS,
);
criarBlocoExpressaoSimples(
"obter_saida",
"SAÍDA",
null,
C.VARIAVEIS,
"O veredito atual",
);
// Contador (índice do loop)
criarBlocoStatementComValor(
"definir_contador",
"definir CONTADOR como",
"VALUE",
null,
C.LOOPS,
);
criarBlocoExpressaoSimples(
"obter_contador",
"CONTADOR",
null,
C.LOOPS,
"Valor atual do contador",
);
// Letra (caractere atual)
criarBlocoStatementComValor(
"definir_letra",
"definir LETRA como",
"VALUE",
null,
C.VARIAVEIS,
);
criarBlocoExpressaoSimples(
"obter_letra",
"LETRA",
null,
C.VARIAVEIS,
"Valor atual da letra",
);
// Constante: alfabeto A-Z
criarBlocoExpressaoSimples(
"alfabeto",
"ALFABETO A-Z",
"String",
C.TEXTO,
"Retorna ABCDEFGHIJKLMNOPQRSTUVWXYZ",
);
// text_charAt — 0-based (sobrescreve o nativo para alinhar com CONTADOR=0)
Blockly.Blocks["text_charAt"] = {
init: function () {
this.setColour(C.TEXTO);
this.setOutput(true, "String");
this.appendValueInput("VALUE")
.setCheck("String")
.appendField("no texto");
this.appendValueInput("AT")
.setCheck("Number")
.appendField("obter letra nº");
this.setInputsInline(true);
this.setTooltip("Letra na posição informada (0 = primeira).");
},
};
// text_indexOf — 0-based (retorna -1 quando não encontra)
Blockly.Blocks["text_indexOf"] = {
init: function () {
this.setColour(C.TEXTO);
this.setOutput(true, "Number");
this.appendValueInput("VALUE")
.setCheck("String")
.appendField("no texto");
this.appendValueInput("FIND")
.setCheck("String")
.appendField("buscar a posição de");
this.setInputsInline(true);
this.setTooltip(
"Primeira posição (0-based) do trecho, ou -1 se não existir.",
);
},
};
};
// ===================== Geradores de código =====================
const defineGenerators = () => {
// Prefix de highlight (faz o bloco piscar na execução passo a passo)
configurarGerador();
// Statements com valor
gerarStatementComValor("definir_saida", "VALUE", (v) => `definirSaida(${v})`);
gerarStatementComValor("definir_contador", "VALUE", (v) =>
`definirContador(${v})`,
);
gerarStatementComValor("definir_letra", "VALUE", (v) => `var letra = ${v}`);
// Expressões
gerarExpressao(
"obter_entrada",
"obterEntrada()",
javascriptGenerator.ORDER_FUNCTION_CALL,
);
gerarExpressao(
"obter_saida",
"obterSaida()",
javascriptGenerator.ORDER_FUNCTION_CALL,
);
gerarExpressao(
"obter_contador",
"obterContador()",
javascriptGenerator.ORDER_FUNCTION_CALL,
);
gerarExpressao("obter_letra", "letra", javascriptGenerator.ORDER_ATOMIC);
gerarExpressao(
"alfabeto",
'"ABCDEFGHIJKLMNOPQRSTUVWXYZ"',
javascriptGenerator.ORDER_ATOMIC,
);
// text_charAt — 0-based
javascriptGenerator.forBlock["text_charAt"] = function (block) {
const text =
javascriptGenerator.valueToCode(
block,
"VALUE",
javascriptGenerator.ORDER_MEMBER,
) || "''";
const at =
javascriptGenerator.valueToCode(
block,
"AT",
javascriptGenerator.ORDER_NONE,
) || "0";
return [`${text}.charAt(${at})`, javascriptGenerator.ORDER_MEMBER];
};
// text_indexOf — 0-based
javascriptGenerator.forBlock["text_indexOf"] = function (block) {
const text =
javascriptGenerator.valueToCode(
block,
"VALUE",
javascriptGenerator.ORDER_MEMBER,
) || "''";
const search =
javascriptGenerator.valueToCode(
block,
"FIND",
javascriptGenerator.ORDER_NONE,
) || "''";
return [`${text}.indexOf(${search})`, javascriptGenerator.ORDER_MEMBER];
};
};