Pular para o quebra-cabeça
Outro dia, me veio à mente a lembrança de um pequeno quebra-cabeça da minha infância, um quebra-cabeça deslizante onde 15 peças quadradas são colocadas em uma moldura em um arranjo de células 4 x 4, deixando um espaço livre. Um conjunto de saliências e ranhuras nas bordas de cada ladrilho e da moldura permite que os ladrilhos deslizem uns sobre os outros enquanto os seguram na moldura. A qualquer momento, qualquer peça adjacente ao espaço livre pode mover-se para esse espaço e, caso contrário, as peças serão impedidas de se mover. Mover uma peça para o espaço livre deixa um novo espaço livre de onde a peça veio e outra peça pode então se mover para esse novo espaço. A ideia é deslizar repetidamente as peças desta forma para organizá-las em uma ordem predeterminada.
Aparentemente, isso é chamado de "15 Puzzle" e existe desde a década de 1870. A pesquisa na web retorna uma série de recriações escritas em uma variedade de linguagens de programação e, de fato, há vários artigos aqui no dev.to, incluindo https://dev.to/artydev/let-us-code-a-sliding-puzzle-9n , https://dev.to/xzanderzone/making-a-slider-puzzle-in-java-script-83m e https://dev.to/claurcia/slide-puzzle-5c55 todos em JavaScript e https:/ /dev.to/mfbmina/building-a-sliding-puzzle-with-go-3bnj em Go. Também é apresentado como um bom desafio inicial para quem está aprendendo JavaScript.
O que despertou meu interesse foi a ideia de que deveria ser recriável na web sem usar nenhuma linguagem de programação! Ou seja, uma implementação usando apenas HTML e CSS puros. Então apresento abaixo. O único compromisso que tive que fazer foi que os 10 jogos fornecidos tivessem posições iniciais pré-embaralhadas fixas.
Para isso, a ordem pré-determinada é mostrar uma imagem completa.
O princípio básico desta implementação é que cada bloco retém um registro de estado de onde está dentro do quadro. Não há muitas maneiras de alterar e manter o estado em HTML e CSS, mas a mais comum é o "hack da caixa de seleção", e essa implementação faz uso intenso dele. Para quem não está familiarizado com o hack da caixa de seleção, quando um elemento
Portanto, cada bloco tem um par de grupos de botões de opção, cada um com quatro botões de opção. Um desses grupos mantém a posição do bloco no eixo X e o outro a sua posição no eixo Y. (Ou posição horizontal e posição vertical, respectivamente, se preferir.) Cada um dos quinze blocos recebe inicialmente uma combinação diferente de coordenadas X e Y por meio de seus botões de opção, de modo que cada um ocupe uma célula diferente no quadro.
Os blocos são inicialmente colocados na célula superior esquerda do quadro e, em seguida, movidos dentro do quadro via CSS, medindo o estado dos botões de opção aplicando uma transformação de tradução a eles:
/* "X" refers to the X-axis cell positions, "Y" to the Y-axis cell positions. * 0, 1, 2, 3 refers to the position on that axis, * 0 = left and top, 3 = right and bottom respectively. */ .tile:has(.X0:checked~.Y0:checked) { transform: translate(0%, 0%); } .tile:has(.X0:checked~.Y1:checked) { transform: translate(0%, 100%); } .tile:has(.X0:checked~.Y2:checked) { transform: translate(0%, 200%); } .tile:has(.X0:checked~.Y3:checked) { transform: translate(0%, 300%); } .tile:has(.X1:checked~.Y0:checked) { transform: translate(100%, 0%); } /* and so on for the remainder of the sixteen combinations */O bloco também contém oito elementos de rótulo, correspondentes aos oito botões de opção. Cada rótulo é posicionado absolutamente sobreposto um ao outro e cada um preenche completamente o ladrilho. Os rótulos são transparentes e são inicialmente configurados para não responder a cliques e toques, definindo pointer-events:none em todos eles.
A próxima etapa é os seletores CSS identificarem onde está a célula vazia. Isso é feito por eliminação, é a célula cujas coordenadas X,Y não são representadas pelo par de grupos de botões de opção de qualquer um dos quinze blocos.
Por exemplo, se corresponder a:
/* "X" refers to the X-axis cell positions, "Y" to the Y-axis cell positions. * 0, 1, 2, 3 refers to the position on that axis, * 0 = left and top, 3 = right and bottom respectively. */ .tile:has(.X0:checked~.Y0:checked) { transform: translate(0%, 0%); } .tile:has(.X0:checked~.Y1:checked) { transform: translate(0%, 100%); } .tile:has(.X0:checked~.Y2:checked) { transform: translate(0%, 200%); } .tile:has(.X0:checked~.Y3:checked) { transform: translate(0%, 300%); } .tile:has(.X1:checked~.Y0:checked) { transform: translate(100%, 0%); } /* and so on for the remainder of the sixteen combinations */então a célula vazia deve estar atualmente na célula do canto superior esquerdo. Repita isso para cada uma das dezesseis células e exatamente uma delas corresponderá.
Uma vez feito isso, as células adjacentes à célula vazia podem ser identificadas. Se a célula vazia estiver em um canto, então há exatamente duas peças que podem se mover para dentro daquela célula, caso contrário, se a célula vazia estiver contra um dos lados da moldura, há três peças que podem se mover para dentro da célula, caso contrário, a célula vazia a célula deve ser uma das quatro células do meio e há quatro peças que podem ser movidas para ela. Para cada um desses blocos, exatamente um dos oito rótulos dos blocos ativará o botão de opção correto necessário para mover o bloco para a célula vazia. Esse rótulo é habilitado definindo seu valor de eventos de ponteiro de volta para automático. Então, como exemplos:
/* "X" refers to the X-axis cell positions, "Y" to the Y-axis cell positions. * 0, 1, 2, 3 refers to the position on that axis, * 0 = left and top, 3 = right and bottom respectively. */ .tile:has(.X0:checked~.Y0:checked) { transform: translate(0%, 0%); } .tile:has(.X0:checked~.Y1:checked) { transform: translate(0%, 100%); } .tile:has(.X0:checked~.Y2:checked) { transform: translate(0%, 200%); } .tile:has(.X0:checked~.Y3:checked) { transform: translate(0%, 300%); } .tile:has(.X1:checked~.Y0:checked) { transform: translate(100%, 0%); } /* and so on for the remainder of the sixteen combinations */A última etapa do jogo é identificar quando o quebra-cabeça é resolvido. Este é simplesmente o caso de verificar se todos os 15 blocos têm os botões de opção esperados dos eixos X e Y definidos para a posição "resolvida".
/* "X" refers to the X-axis cell positions, "Y" to the Y-axis cell positions. * 0, 1, 2, 3 refers to the position on that axis, * 0 = left and top, 3 = right and bottom respectively. */ .tile:has(.X0:checked~.Y0:checked) { transform: translate(0%, 0%); } .tile:has(.X0:checked~.Y1:checked) { transform: translate(0%, 100%); } .tile:has(.X0:checked~.Y2:checked) { transform: translate(0%, 200%); } .tile:has(.X0:checked~.Y3:checked) { transform: translate(0%, 300%); } .tile:has(.X1:checked~.Y0:checked) { transform: translate(100%, 0%); } /* and so on for the remainder of the sixteen combinations */O resto é cosmético. O deslizamento é feito com uma simples transição da transformação descrita acima
/* "X" refers to the X-axis cell positions, "Y" to the Y-axis cell positions. * 0, 1, 2, 3 refers to the position on that axis, * 0 = left and top, 3 = right and bottom respectively. */ .tile:has(.X0:checked~.Y0:checked) { transform: translate(0%, 0%); } .tile:has(.X0:checked~.Y1:checked) { transform: translate(0%, 100%); } .tile:has(.X0:checked~.Y2:checked) { transform: translate(0%, 200%); } .tile:has(.X0:checked~.Y3:checked) { transform: translate(0%, 300%); } .tile:has(.X1:checked~.Y0:checked) { transform: translate(100%, 0%); } /* and so on for the remainder of the sixteen combinations */e cada bloco mostra uma parte da imagem do jogo usando tamanho e posição de fundo
/* "X" refers to the X-axis cell positions, "Y" to the Y-axis cell positions. * 0, 1, 2, 3 refers to the position on that axis, * 0 = left and top, 3 = right and bottom respectively. */ .tile:has(.X0:checked~.Y0:checked) { transform: translate(0%, 0%); } .tile:has(.X0:checked~.Y1:checked) { transform: translate(0%, 100%); } .tile:has(.X0:checked~.Y2:checked) { transform: translate(0%, 200%); } .tile:has(.X0:checked~.Y3:checked) { transform: translate(0%, 300%); } .tile:has(.X1:checked~.Y0:checked) { transform: translate(100%, 0%); } /* and so on for the remainder of the sixteen combinations */e há um único conjunto de botões de opção para escolher qual dos dez jogos jogar.
Para jogar, basta clicar ou tocar no bloco que deseja deslizar para a célula vazia.
Também forneci um modo "barebones" para mostrar as letras dos blocos e os botões de opção, o que pode ajudar na compreensão de como o HTML e o CSS funcionam.
Então aqui está o jogo completo. Por favor, deixe-me saber qualquer comentário que você tenha.
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3