Langkau ke teka-teki
Pada hari yang lain, terlintas dalam fikiran saya tentang mainan teka-teki kecil dari zaman kanak-kanak saya, teka-teki gelangsar di mana 15 jubin persegi diletakkan dalam bingkai dalam susunan 4 x 4 sel meninggalkan satu ruang kosong. Satu set rabung dan alur pada tepi setiap jubin dan bingkai membolehkan jubin meluncur melepasi satu sama lain sambil memegang jubin dalam bingkai. Pada bila-bila masa mana-mana jubin bersebelahan dengan ruang kosong boleh bergerak ke dalam ruang itu, dan sebaliknya jubin dihalang daripada bergerak. Memindahkan jubin ke dalam ruang kosong kemudian meninggalkan ruang kosong baharu di mana jubin itu berasal dan jubin lain kemudiannya boleh berpindah ke ruang baharu itu. Ideanya ialah dengan menggelongsor berulang jubin dengan cara ini untuk menyusun jubin mengikut susunan yang telah ditetapkan.
Nampaknya ini dipanggil "15 Puzzle" dan telah wujud sejak tahun 1870-an. Mencari di web mengembalikan beberapa rekreasi yang ditulis dalam pelbagai bahasa pengaturcaraan dan sememangnya terdapat beberapa artikel di sini di dev.to termasuk https://dev.to/artydev/let-us-code-a-sliding-puzzle-9n , https://dev.to/xzanderzone/making-a-slider-puzzle-in-java-script-83m, dan https://dev.to/claurcia/slide-puzzle-5c55 semuanya dalam JavaScript dan https:/ /dev.to/mfbmina/building-a-sliding-puzzle-with-go-3bnj dalam Go. Ia juga dipersembahkan sebagai cabaran permulaan yang baik untuk mereka yang mempelajari JavaScript.
Apa yang menarik minat saya ialah idea bahawa ia sepatutnya boleh dibuat semula di web tanpa menggunakan bahasa pengaturcaraan sama sekali! Iaitu, pelaksanaan hanya menggunakan HTML dan CSS tulen. Jadi saya bentangkan di bawah. Satu kompromi yang perlu saya lakukan ialah 10 permainan yang disediakan telah menetapkan kedudukan permulaan pra-kocok.
Untuk ini, pesanan yang telah ditetapkan adalah untuk menunjukkan gambar yang lengkap.
Prinsip asas pelaksanaan ini ialah setiap jubin mengekalkan rekod keadaan di mana ia berada dalam bingkai. Tidak banyak cara untuk menukar dan menahan keadaan dalam HTML dan CSS, tetapi yang paling biasa ialah "godam kotak semak" dan pelaksanaan ini banyak menggunakannya. Bagi sesiapa yang tidak biasa dengan penggodaman kotak pilihan, apabila
Jadi setiap jubin mempunyai sepasang kumpulan butang radio setiap satu dengan empat butang radio. Satu daripada kumpulan ini mengekalkan kedudukan jubin dalam paksi-X dan satu lagi kedudukannya dalam paksi-Y. (Atau kedudukan mendatar dan kedudukan menegak masing-masing, jika anda mahu.) Lima belas jubin setiap satu pada mulanya diberi gabungan koordinat X dan Y yang berbeza melalui butang radio mereka supaya setiap satu menduduki sel yang berbeza dalam bingkai.
Jubin pada mulanya diletakkan di bahagian atas bingkai, sel kiri dan kemudian dialihkan dalam bingkai melalui CSS yang mengukur keadaan butang radio dengan menggunakan transformasi terjemahan padanya:
/* "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 */
Jubin itu kemudiannya juga mengandungi lapan elemen label, sepadan dengan lapan butang radio. Setiap label diposisikan mutlak menindih satu sama lain dan setiap satu memenuhi jubin sepenuhnya. Label adalah telus dan pada mulanya disediakan untuk tidak bertindak balas kepada klik dan ketik dengan menetapkan pointer-events:none pada kesemuanya.
Langkah seterusnya ialah pemilih CSS mengenal pasti di mana sel kosong itu berada. Ini dilakukan dengan penyingkiran, ia adalah sel yang koordinat X,Ynya tidak diwakili oleh pasangan kumpulan butang radio bagi mana-mana daripada lima belas jubin.
Contohnya, jika ini sepadan dengan:
.frame:not(:has(.tile .X0:checked~.Y0:checked)) { .... }
maka sel kosong mesti berada di sel penjuru kiri sebelah atas pada masa ini. Ulangi ini untuk setiap enam belas sel dan tepat satu daripadanya akan sepadan.
Setelah itu selesai, sel-sel yang bersebelahan dengan sel kosong boleh dikenal pasti. Jika sel kosong berada di sudut, maka terdapat betul-betul dua jubin yang boleh bergerak ke dalam sel itu, sebaliknya, jika sel kosong bertentangan dengan salah satu sisi bingkai, terdapat tiga jubin yang boleh bergerak ke dalam sel, jika tidak, sel kosong itu sel mestilah salah satu daripada empat sel tengah, dan terdapat empat jubin yang boleh bergerak kepadanya. Untuk setiap jubin tersebut, tepat satu daripada lapan label jubin akan mengaktifkan butang radio yang betul yang diperlukan untuk mengalihkan jubin ke sel kosong. Label itu didayakan dengan menetapkan nilai acara penudingnya kembali kepada auto. Jadi sebagai contoh:
/* Top, left corner */ .frame:not(:has(.tile .X0:checked ~ .Y0:checked)) { :is( .tile:has(.X0:checked ~ .Y1:checked) label.Y0, .tile:has(.X1:checked ~ .Y0:checked) label.X0 ) { pointer-events: auto; } } /* right most cell of row two */ .frame:not(:has(.tile .X1:checked ~ .Y3:checked)) { :is( .tile:has(.X1:checked ~ .Y2:checked) label.Y3, .tile:has(.X0:checked ~ .Y3:checked) label.X1, .tile:has(.X2:checked ~ .Y3:checked) label.X1 ) { pointer-events: auto; } } /* second cell from left on row three */ .frame:not(:has(.tile .X2:checked ~ .Y1:checked)) { :is( .tile:has(.X2:checked ~ .Y0:checked) label.Y1, .tile:has(.X2:checked ~ .Y2:checked) label.Y1, .tile:has(.X1:checked ~ .Y1:checked) label.X2, .tile:has(.X3:checked ~ .Y1:checked) label.X2 ) { pointer-events: auto; } }
The last step of the game is to identify when the puzzle is solved. This is simply a case of checking that the 15 tiles all have their expected X and Y axis radio buttons set to their "solved" position.
/* Each tile is assigned a letter "a" to "o". * The puzzle is solved when the tiles are in alphabetical order * reading left to right and top to bottom */ .frame:has(.a .X0:checked ~ .Y0:checked):has(.b .X1:checked ~ .Y0:checked):has( .c .X2:checked ~ .Y0:checked ):has(.d .X3:checked ~ .Y0:checked):has(.e .X0:checked ~ .Y1:checked):has( .f .X1:checked ~ .Y1:checked ):has(.g .X2:checked ~ .Y1:checked):has(.h .X3:checked ~ .Y1:checked):has( .i .X0:checked ~ .Y2:checked ):has(.j .X1:checked ~ .Y2:checked):has(.k .X2:checked ~ .Y2:checked):has( .l .X3:checked ~ .Y2:checked ):has(.m .X0:checked ~ .Y3:checked):has(.n .X1:checked ~ .Y3:checked):has( .o .X2:checked ~ .Y3:checked ) ~ .options .success { display: block; }
The rest is cosmetic. The sliding is done with a simple transition of the transform described above
.tile { transition: 0.5s transform; @media (prefers-reduced-motion) { transition: none; } }
and each tile shows a portion of the game's image using background-size and background-position
.tile { background-size: 400%; } #board1 .tile { background-image: url("https://alohci.net/image/dev.to/slidergame/mullermarc-k7bQqdUf954-unsplash.webp"); } .a { background-position: 0% 0%; } .b { background-position: 33.333% 0%; } .c { background-position: 66.667% 0%; } .d { background-position: 100% 0%; } .e { background-position: 0% 33.333%; } /* and so on for the remaining tiles */
and there's a single set of radio buttons to choose which of the ten games to play.
To play the game, simply click or tap on the tile you want to slide to the empty cell.
I've also provided a "barebones" mode to show the tile letters and radio buttons which might help with the understanding of how the HTML and CSS works.
So here's the completed puzzle game. Please let me know any feedback you have.
Atas ialah kandungan terperinci Permainan peluncur \'Teka-teki\' dalam HTML dan CSS. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!