Friday JS: quine yang memainkan tic-tac-toe

Saya menyapa semua orang di bagian tradisional saya yang penuh dengan kegilaan Lovecraftian.

Dalam proses penulisan salah satu artikel sebelumnya (jangan lihat, itu tidak terlalu baik), saya memikirkan fakta bahwa quine ... Ya, untuk berjaga-jaga, saya ingatkan Anda: quine adalah program yang menampilkan teksnya sendiri, dan melakukannya "dengan jujur" ( tanpa melihat, misalnya, teks ini di file di hard drive Anda). Secara umum, puzomerka berarti tradisional programmer.

Jadi, saya memikirkan fakta bahwa quine, pada prinsipnya, dapat membawa muatan sewenang-wenang. Yaitu, melakukan hal lain selain fungsi utamanya. Dan sebagai pembuktian konsep, saya memutuskan untuk menulis quine yang memainkan tic-tac-toe. Dan menulis. Detail kotor di bawah potongan.

gambar

"Tapi bagaimana dia bisa melakukan hal lain selain menampilkan teksnya?" - mungkin Anda bertanya. Dan mudah. Selain output, program ini juga memiliki input. Jika program menampilkan teksnya tanpa input - maka itu quine. Jika program melakukan hal lain ketika input tersedia, siapakah kita untuk mengutuknya?

Mungkin saya akan segera mengeluarkan kartu di atas meja. Berikut ini tautan ke kreasi saya. Ada tiga entitas dalam file dengan referensi:

  1. Fungsi quine adalah apa yang saya bicarakan.
  2. Fungsi evalAndCall adalah pembantu.
  3. Kelas permainan - bahkan lebih banyak pembantu

Pertama, kita akan berbicara tentang cara bekerja dengan fungsi quine, dan kemudian cara kerjanya.

Bagaimana cara bekerja dengannya


Di awal fungsi quine, Anda dapat melihat yang berikut:

function quine(input){/* |1|2|3| |4|x|6| |7|8|9| !  - ,     -. ,   .     ,    ,     .             ,    . :   - 0   - 0  - 0 */ 

Komentar di bagian paling awal dari fungsi adalah antarmuka pengguna. Melalui itu, fungsi akan berkomunikasi dengan mereka yang menggunakannya untuk permainan. Pada awalnya, saya berpikir untuk melakukannya melalui konsol, tetapi kemudian saya memutuskan bahwa akan lebih benar untuk menjaga fungsinya tetap bersih .

Mari kita periksa apakah fungsinya benar-benar quine. Di sini, untuk kenyamanan, saya memposting (hampir) halaman HTML kosong dengan skrip quine.js terlampir. Membuka alat pengembang, Anda dapat mengarahkan kode berikut secara non-selektif di sana:

 const quineText = quine(); const evaluatedQuine = eval("(" + quineText + ")"); // ,     eval       //  undefined,   const evaluatedQuineText = evaluatedQuine(); quineText == evaluatedQuineText; // true 

Bore mod
Sebenarnya, tentu saja, kami hanya memeriksa bahwa fungsi quine mengembalikan teks quine, dan bukan bahwa itu sendiri adalah quine. Dan lebih tepatnya - kami hanya memeriksa bahwa fungsi quine mengembalikan teks fungsi, yang dalam kasus kami berfungsi seperti quine. Tidak ada jaminan bahwa di dalam tidak mengandung sesuatu seperti:

 if(Math.random() < 0.99){ beAGoodQuine(); }else{ haltAndCatchFire(); } 


Sekarang kita bisa mencoba bermain dengannya. Katakanlah kita melakukan langkah pertama ke sudut kiri atas bidang.

 let quineText = quine(1); 

Sekarang "antarmuka pengguna" adalah sebagai berikut:

 function quine(input){/* |o|x|3| |4|x|6| |7|8|9| !  - ,     -. ,   .     ,    ,     .             ,    . :   - 0   - 0  - 0 */ 

Quine memperhitungkan langkah kami dan membuat respons di bidang tengah atas. By the way, fungsi yang dihasilkan juga disebut quine - tanpa argumen, itu akan mengembalikan teks barunya, dan bukan teks dari fungsi aslinya. Kita dapat memainkan seluruh permainan, tetapi untuk kenyamanan kita akan menggunakan fungsi tambahan evalAndCall.

 let quineText = quine(); //         ,    . /* |1|2|3| |4|x|6| |7|8|9| */ quineText = evalAndCall(quineText, 1) /* |x|o|3| |4|x|6| |7|8|9| */ quineText = evalAndCall(quineText, 3) /* |x|o|o| |4|x|6| |7|8|x|     .    ,   0    */ quineText = evalAndCall(quineText, 0) /* |1|2|3| |4|x|6| |7|8|9| :   - 0   - 1  - 0 */ 

Voila! Quine memainkan, menang, dan bahkan skor. Anda bisa bermain dengannya lebih lama, tetapi untuk kenyamanan yang lebih besar, saya sarankan menggunakan kelas Game, yang dengannya saya menguji sendiri game tersebut. Saya pikir jika Anda membaca sampai titik ini, saya tidak perlu menjelaskan bagaimana cara menggunakannya.

Bagaimana cara kerjanya


Secara umum, tugas itu terdiri dari dua bagian: menulis, jika mungkin, fungsi singkat dari permainan tic-tac-toe, kemudian mendorongnya ke dalam quine. Mari kita mulai dengan tic-tac-toe.

Inti dari "kecerdasan buatan" ada pada garis 66-90 dan siluetnya menyerupai tupai yang keras kepala:

  const rules = { "o___x____": "ox__x__!_", "ox__x__o_": "ox!_x_xo_", "oxo_x_xo_": "oxo!xxxo_", "oxooxxxo_": "oxooxxxoxd", "_o__x____": "xo__x___!", "xo__x___o": "xo_xx!!_o" }; const next = (field, move) => { if(!~"!_".indexOf(field[--move])){ return null; } field[move] = "o"; const win = field.indexOf("!"); if(~win){ field[win] = "x"; return [...field, "w"]; } for(let n = 0; n < 4; n++){ field = field.map((_, i) => field[[2, 5, 8, 1, 4, 7, 0, 3, 6][i]]); rules[field.join("")] && (field = rules[field.join("")].split("")); } return field; } 

Kode ini terlihat agak dikaburkan karena saya tertarik membuatnya sesingkat mungkin. Esensinya adalah sebagai berikut: keadaan lapangan - array sembilan elemen - dan jumlah sel tempat pemain-orang bergerak (selanjutnya memeriksa validitas gerakan ini), memasuki fungsi berikutnya. Setiap elemen bidang dapat berupa salib, nol, garis bawah (sel kosong) atau tanda seru (sel kosong untuk meletakkan tanda silang pada kesempatan pertama). Jika ada tanda seru di lapangan, kami segera bergerak di sana dan menang. Jika tidak, kami merekatkan array menjadi string dan mencari aturan terkait di objek aturan. Untuk menghemat ruang, aturannya akurat untuk rotasi, oleh karena itu, untuk mencari, bidang tersebut diputar empat kali. Ketika aturan yang diinginkan ditemukan, keadaan bidang digantikan oleh nilai aturan, dipecah menjadi karakter. Selanjutnya, fungsi berikutnya mengembalikan keadaan baru bidang tersebut. Pada saat yang sama, karakter kesepuluh tambahan dapat bergabung: "w" - jika AI menang, "d" - jika ada hasil seri.

Berkat tanda seru, "petunjuk" dan rotasi lapangan, dan juga, tentu saja, karena fakta bahwa AI bergerak terlebih dahulu, strategi optimal dijelaskan hanya dalam 6 aturan.

Menggunakan fungsi berikutnya, fungsi quine memproses input dan menulis beberapa bidang ke objek magicHash. Dan di sini kita dengan lancar beralih ke bagian kedua: bagaimana komponen "quayne" bekerja. Semua sihir ada di objek magicHash dan properti magicString.

Baris magicString, seperti yang dapat Anda lihat dengan mudah, berisi teks program yang hampir sepenuhnya digandakan. Namun, seperti yang diketahui oleh semua orang yang pernah mencoba menulis quine, Anda tidak dapat memasukkan teks yang sepenuhnya lengkap ke dalamnya - karena dengan demikian teks tersebut harus benar-benar berisi dirinya sendiri, yang tidak mungkin untuk string dengan panjang terbatas. Oleh karena itu, selain teks "biasa", teks ini juga berisi urutan wildcard "ajaib" yang dibatasi di kedua sisi oleh karakter "$".

Ketika saatnya tiba X dan kita harus mengembalikan teks fungsi, kita hanya mengambil magicString dan mengganti urutan wildcard di dalamnya dengan properti yang sesuai dari objek magicHash. Properti ini bisa statis (backtick), berubah saat runtime (bidang) atau bahkan ditambahkan saat runtime (pesan) - itu tidak masalah. Adalah penting bahwa untuk setiap bagian kode yang "bermasalah" yang tidak dapat hanya diduplikasi dalam satu baris, kita memiliki properti "ajaib" dari objek magicHash. Yang terakhir menggantikan magicString sendiri. Yang terakhir - karena kalau tidak akan ada urutan wildcard tambahan yang juga akan diganti.

Ringkasan


Saya memeriksa dari pengalaman saya sendiri bahwa Anda dapat memasukkan apa saja ke dalam quine. Pada prinsipnya, jika Anda repot-repot, Anda dapat membuat generator quine - fungsi yang mengubah fungsi murni lainnya menjadi quine dan memungkinkan Anda untuk menyimpan keadaan sewenang-wenang yang dapat diakses oleh fungsi murni di atas. Namun, margin naskah ini terlalu sempit ...

Secara umum, saya tidak berpura-pura dengan kebaruan khusus dari "penemuan" saya. Para fungsionaris hardcore pasti telah membaca teks ini dengan senyum keunggulan. Tetapi menarik bagi saya untuk mengambilnya dengan tangan saya sendiri, untuk menempatkan, dengan kata lain, jari ke luka. Dan saya harap Anda tertarik membacanya.

Selamat tinggal, cewek dan cowok. Sampai jumpa lagi.

Source: https://habr.com/ru/post/id435732/


All Articles