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).
250 lines
6.3 KiB
JavaScript
250 lines
6.3 KiB
JavaScript
/**
|
|
* @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];
|
|
};
|
|
};
|