
Era uma vez, as pessoas criaram a linguagem XML e viram que era boa. E eles começaram a usá-lo sempre que possível, e mesmo onde não deveria. Formatos para armazenamento e transferência de dados, configurações, serviços web, bancos de dados ... Parecia dar uma olhada - XML, XML em todos os lugares. O tempo passou, as pessoas mudaram de idéia, inventaram vários outros formatos de dados (ou ocultaram o XML dentro dos arquivos), e a loucura do XML pareceu se acalmar. Mas desde então, quase qualquer sistema conseguiu XML e integrar tais sistemas (quem disse que o Apache Camel?) É a melhor e mais fácil maneira de usar documentos XML.
Onde XML está, existe XSLT, uma linguagem projetada para transformar documentos XML. Essa linguagem é especializada, mas tem a propriedade de ser
completa, de acordo com Turing . Portanto, o idioma é adequado para uso "anormal". Aqui, por exemplo, há uma
solução para o problema de 8 rainhas . Então, você pode escrever um jogo.
Para os impacientes: um programa de trabalho no
JSFiddle , fontes no
GitHub .
Qualquer programa converte entrada em saída. É possível distinguir três partes no programa: pré-processador, processador e pós-processador. O pré-processador prepara os dados de entrada para processamento adicional. O processador está envolvido no principal trabalho de conversão de dados, se necessário, "misturando" entrada do usuário e sinais e eventos externos, inclusive em um ciclo. É necessário um pós-processador para converter os resultados do processador em um formato adequado à percepção humana (ou por outros programas).

No caso de um jogo interativo no XSLT, cada uma das três partes do programa será um arquivo XSLT separado. O pré-processador preparará o campo de jogo. O processador aplicará o movimento do jogador humano, fará o movimento do jogador do computador e determinará o vencedor. O pós-processador renderizará o estado do jogo.
Um programa XSLT precisa de um tempo de execução. O tempo de execução mais comum capaz de executar o XSLT é qualquer
navegador moderno . Usaremos o XSLT versão 1.0, pois ele é compatível com os navegadores prontos para uso.
Um pouco sobre XSLT e XPath
XSLT é uma linguagem de transformação de documento XML; XPath é usado para acessar partes de um documento XML. As especificações para esses idiomas estão publicadas no w3.org:
XSLT versão 1.0 e
XPath versão 1.0 .
Os exemplos básicos e de uso do XSLT e XPath são facilmente pesquisados na rede. Aqui, chamo a atenção para os recursos que precisam ser considerados ao tentar usar o XSLT como uma linguagem de programação de alto nível para uso geral "normal".
O XSLT nomeou funções. Eles são declarados um elemento.
<xsl:template name="function_name"/>
e são chamados assim:
<xsl:call-template name="function_name"/>
Funções podem ter parâmetros.
Anúncio:
<xsl:template name="add"> <xsl:param name="x"/> <xsl:param name="y"/> <xsl:value-of select="$x + $y"/> </xsl:template>
Chamada de função com parâmetros:
<xsl:call-template name="add"> <xsl:with-param name="x" select="1"/> <xsl:with-param name="y" select="2"/> </xsl:call-template>
Os parâmetros podem ter valores padrão.
Os parâmetros podem ser "globais" vindos de fora. Usando esses parâmetros, a entrada do usuário será transferida para o programa.
O idioma também permite que você declare variáveis que podem estar associadas a um valor. Parâmetros e variáveis são imutáveis e os valores podem ser atribuídos a eles uma vez (como em Erlang, por exemplo).
XPath define quatro tipos de dados básicos: string, número, booleano e conjunto de nós. O XSLT adiciona um quinto tipo - um fragmento da árvore de resultados. Esse fragmento se parece com um conjunto de nós, mas com ele você pode executar um conjunto limitado de operações. Ele pode ser copiado inteiramente no documento de saída XML, mas você não pode acessar os nós filhos.
<xsl:variable name="board"> <cell>1</cell> <cell>2</cell> <cell>3</cell> <cell>4</cell> </xsl:variable>
A variável board contém um fragmento do documento XML. Mas os nós filhos não podem ser acessados. Este código não é válido:
<xsl:for-each select="$board/cell"/>
O melhor que você pode obter é acessar os nós de texto do fragmento e trabalhar com eles como uma string:
<xsl:value-of select="substring(string($board), 2, 1)"/>
retornará "2".
Por isso, em nosso jogo, o tabuleiro (ou campo de jogo) será apresentado como uma seqüência de caracteres para que possa ser manipulado arbitrariamente.
O XSLT permite iterar um conjunto de nós usando a construção xsl: for-each. Mas o idioma não tem os habituais loops para ou enquanto. Em vez disso, você pode usar uma chamada de função recursiva (iteração e recursão são isomórficas). Um loop do formulário para x em a..b será organizado assim:
<xsl:call-template name="for_loop"> <xsl:with-param name="x" select="$a"/> <xsl:with-param name="to" select="$b"/> </xsl:call-template> <xsl:template name="for_loop"> <xsl:param name="x"/> <xsl:param name="to"/> <xsl:if test="$x < $to"> <xsl:call-template name="for_loop"> <xsl:with-param name="x" select="$x + 1"/> <xsl:with-param name="to" select="$to"/> </xsl:call-template> </xsl:if> </xsl:template>
Escrevendo um tempo de execução
Para que o programa funcione, é necessário: 3 XSLT, XML de origem, entrada do usuário (parâmetros), XML de estado interno e XML de saída.
Colocamos campos de texto com identificadores no
arquivo html : "preprocessor-xslt", "processador-xslt", "postprocessor-xslt", "input-xml", "parameters", "output-xml", "postprocessed-xml". Também colocamos /> para incorporar o resultado na página (para visualização).
Adicione dois botões: inicialização e chamada (etapa) do processador.
Vamos escrever um código JavaScript.
Um recurso importante é o uso da transformação XSLT. function transform(xslt, xml, params) { var processor = new XSLTProcessor(); var parser = new DOMParser(); var xsltDom = parser.parseFromString(xslt, "application/xml");
Funções para executar o pré-processador, processador e pós-processador: function doPreprocessing() { var xslt = document.getElementById("preprocessor-xslt").value; var xml = document.getElementById("input-xml").value; var result = transform(xslt, xml); document.getElementById("output-xml").value = result; } function doProcessing() { var params = parseParams(document.getElementById("parameters").value); var xslt = document.getElementById("processor-xslt").value; var xml = document.getElementById("output-xml").value; var result = transform(xslt, xml, params); document.getElementById("output-xml").value = result; } function doPostprocessing() { var xslt = document.getElementById("postprocessor-xslt").value; var xml = document.getElementById("output-xml").value; var result = transform(xslt, xml); document.getElementById("postprocessed-xml").value = result; document.getElementById("output").innerHTML = result; }
A função auxiliar parseParams () analisa a entrada do usuário em pares chave = valor.
O botão de inicialização chama function onInit() { doPreprocessing(); doPostprocessing(); }
Botão Iniciar do processador function onStep() { doProcessing(); doPostprocessing(); }
O tempo de execução básico está pronto.
Como usá-lo. Insira três documentos XSLT nos campos apropriados. Inserir documento de entrada XML. Pressione o botão "Init". Se necessário, insira os valores necessários no campo de parâmetro. Clique no botão Step.
Escrevendo um jogo
Se outra pessoa não adivinhou, o jogo interativo do título é o clássico jogo da velha por 3 por 3.
O campo de jogo é uma tabela 3 por 3, cujas células são numeradas de 1 a 9.
O jogador humano sempre cruza (o símbolo "X"), o computador - zera ("O"). Se a célula estiver ocupada por uma cruz ou um zero, o dígito correspondente será substituído pelo símbolo "X" ou "O".
O estado do jogo está contido em um documento XML deste formulário:
<game> <board>123456789</board> <state></state> <beginner></beginner> <message></message> </game>
O elemento <board /> contém o campo de jogo; <estado /> - o estado do jogo (ganhar um dos jogadores ou empate ou erro); o elemento <iniciante /> é usado para determinar quem iniciou o jogo atual (para que outro jogador inicie o próximo jogo); <message /> - mensagem para o jogador.
O pré-processador gera um estado inicial (campo vazio) a partir de um documento XML arbitrário.
O processador valida a entrada do usuário, aplica seu movimento, calcula o estado do jogo, calcula e aplica o movimento do computador.
No pseudo-código, parece algo como isto fn do_move() { let board_after_human_move = apply_move(board, "X", param) let state_after_human_move = get_board_state(board_after_human_move) if state_after_human_move = "" { let board_after_computer_move = make_computer_move(board_after_human_move) let state_after_computer_move = get_board_state(board_after_computer_move) return (board_after_computer_move, state_after_computer_move) } else { return (board_after_human_move, state_after_human_move) } } fn apply_move(board, player, index) { // board index player } fn get_board_state(board) { // "X", , "O", , "tie" } fn make_computer_move(board) { let position = get_the_best_move(board) return apply_move(board, "O", position) } fn get_the_best_move(board) { return get_the_best_move_loop(board, 1, 1, -1000) } fn get_the_best_move_loop(board, index, position, score) { if index > 9 { return position } else if cell_is_free(board, index) { let new_board = apply_move(board, "O", index) let new_score = minimax(new_board, "X", 0) if score < new_score { return get_the_best_move_loop(board, index + 1, index, new_score) } else { return get_the_best_move_loop(board, index + 1, position, score) } } else { return get_the_best_move_loop(board, index + 1, position, score) } } fn cell_is_free(board, index) { // true, board index ( ) } fn minimax(board, player, depth) { let state = get_board_state(board) if state = "X" { // return -10 + depth } else if state = "O" { // return 10 - depth } else if state = "tie" { // return 0 } else { let score = if player = "X" { 1000 } else { -1000 } return minimax_loop(board, player, depth, 1, score) } } fn minimax_loop(board, player, depth, index, score) { if index > 9 { return score } else if cell_is_free(board, index) { // , let new_board = apply_move(board, player, index) let new_score = minimax(new_board, switch_player(player), depth + 1) let the_best_score = if player = "X" { // if new_score < score { new_score } else { score } } else { // if new_score > score { new_score } else { score } } return minimax_loop(board, player, depth, index + 1, the_best_score) } else { // return minimax_loop(board, player, depth, index + 1, score) } } fn switch_player(player) { // ; X -> O, O -> X }
A função de seleção de movimentação do computador usa o algoritmo minimax, onde o computador maximiza sua pontuação e a pessoa a minimiza. O parâmetro de profundidade da função minimax é necessário para selecionar um movimento que leva à vitória no menor número de movimentos.
Esse algoritmo usa um grande número de chamadas recursivas e o primeiro movimento do computador é calculado na minha máquina em até 2-3 segundos. De alguma forma, precisamos acelerar. Você pode simplesmente fazer e pré-calcular as melhores jogadas do computador para todas as possíveis condições aceitáveis do campo de jogo. Esses estados acabaram sendo 886. É possível reduzir esse número devido a rotações e reflexões do campo, mas não é necessário.
A nova versão é rápida.
É hora de exibir lindamente o campo de jogo. O que usar se isso for algo a) deve desenhar gráficos (século XXI no quintal, que tipo de jogo sem gráficos?!) Eb) é desejável ter o formato XML? Claro SVG!
O pós-processador desenha um campo quadriculado e organiza cruzes verdes, zeros azuis e pequenos
vestidos pretos
nele . Também mostra mensagens sobre o final do jogo.
E agora parece que o jogo está pronto. Mas algo não está certo. Para jogar, você precisa executar muitas ações desnecessárias, chatas e irritantes: digite o número da célula no campo para o próximo código e pressione o botão. Basta clicar na célula desejada!
Estamos finalizando o
tempo de execução e o
pós -
processador .
No tempo de execução, adicione a função de resposta clicando no elemento SVG:
function onSvgClick(arg) { document.getElementById("parameters").value = arg; onStep(); }
No pós-processador, adicionamos um quadrado acima de cada célula (a transparência é especificada pelo estilo ret.btn), quando clicada, uma função com o número da célula é chamada:
<rect class="btn" x="-23" y="-23" width="45" height="45" onclick="onSvgClick({$index})"/>
Após a conclusão do lote, clicar em qualquer célula inicia uma nova. O botão "Init" precisa ser pressionado apenas uma vez no início.
Agora você pode considerar o jogo terminado. A coisa é pequena: esconda o interior, coloque-o em um aplicativo de elétrons, coloque-o no Steam, acorde rico e famoso.
Conclusão
Um programador de espírito forte pode escrever qualquer coisa em qualquer coisa,
mesmo em JavaScript . Mas é melhor usar uma ferramenta adequada para cada tarefa.