Saya membuat implementasi MiniMax sederhana dalam bahasa pemrograman fungsional Elixir. Karena ada banyak game pengetahuan sempurna (tic tac toe, connect-four, checker, catur, dll), implementasi ini bisa menjadi kerangka kerja untuk membuat AI game untuk game-game ini.
Namun, satu masalah yang saya hadapi adalah bagaimana cara menyimpan kondisi permainan dengan benar dalam bahasa fungsional. Game-game ini sebagian besar berurusan dengan papan permainan dua dimensi, di mana operasi berikut sering dilakukan:
- Baca isi lokasi papan tertentu
- Perbarui konten lokasi papan tertentu (saat mengembalikan kemungkinan langkah baru)
- Mempertimbangkan isi dari satu atau lebih lokasi yang terhubung ke lokasi saat ini (yaitu lokasi horisontal, vertikal atau diagonal berikutnya atau sebelumnya)
- Mempertimbangkan isi dari beberapa lokasi yang terhubung ke segala arah.
- Mempertimbangkan isi dari seluruh file, pangkat dan diagonal.
- Memutar atau menyalin papan (untuk memeriksa simetri yang memberikan hasil yang sama dengan sesuatu yang sudah dihitung).
Sebagian besar bahasa fungsional menggunakan Linked Lists dan Tuples sebagai blok bangunan dasar dari struktur data multi-elemen. Namun, ini tampaknya dibuat sangat buruk untuk pekerjaan itu:
- Daftar tertaut memiliki waktu pencarian O (n) (linier). Juga, karena kita tidak dapat 'memindai dan memperbarui papan' dalam sekali sapuan di papan, menggunakan daftar tampaknya sangat tidak praktis.
- Tuples memiliki waktu pencarian O (1) (konstan). Namun, merepresentasikan papan sebagai tuple ukuran tetap membuatnya sangat sulit untuk beralih ke peringkat, file, diagonal, atau jenis kotak lainnya secara berurutan. Juga, baik Elixir, dan Haskell (yang merupakan dua bahasa fungsional yang saya tahu) tidak memiliki sintaks untuk membaca elemen ke- n sebuah tuple. Ini akan membuat mustahil untuk menulis solusi dinamis yang akan bekerja untuk dewan dengan ukuran sewenang-wenang.
Elixir memiliki struktur data Peta bawaan (dan Haskell memiliki Data.Map
) yang memungkinkan O (log n) (logaritmik) akses ke elemen. Saat ini saya menggunakan peta, dengan x, y
tupel yang mewakili posisi sebagai kunci.
Ini 'berhasil' tapi rasanya salah dengan penyalahgunaan peta dengan cara ini, meskipun saya tidak tahu persis mengapa. Saya mencari cara yang lebih baik untuk menyimpan papan permainan dua dimensi dalam bahasa pemrograman fungsional.
Jawaban:
A
Map
adalah struktur data dasar yang tepat di sini. Saya tidak yakin mengapa itu akan membuat Anda gelisah. Ini memiliki waktu pencarian dan pembaruan yang baik, ukurannya dinamis, dan sangat mudah untuk membuat struktur data turunan. Misalnya (dalam haskell):Hal lain yang sering sulit dipahami oleh para programmer ketika mereka pertama kali memulai pemrograman dengan kekekalan penuh adalah Anda tidak perlu hanya berpegang pada satu struktur data saja. Anda biasanya memilih satu struktur data sebagai "sumber kebenaran", tetapi Anda dapat membuat turunan sebanyak yang Anda inginkan, bahkan turunan dari turunan, dan Anda tahu mereka akan tetap disinkronkan selama Anda membutuhkannya.
Itu berarti Anda dapat menggunakan a
Map
di tingkat paling atas, lalu beralih keLists
atauArrays
untuk analisis baris, laluArrays
diindeks dengan cara lain untuk analisis kolom, kemudian bitmask untuk analisis pola, laluStrings
untuk tampilan. Program fungsional yang dirancang dengan baik tidak melewatkan struktur data tunggal. Mereka adalah serangkaian langkah yang mengambil satu struktur data dan memancarkan yang baru yang cocok untuk langkah selanjutnya.Selama Anda bisa keluar dari sisi lain dengan gerakan dalam format yang bisa dipahami oleh tingkat atas, Anda tidak perlu khawatir seberapa besar Anda merestrukturisasi data di antaranya. Itu tidak berubah, jadi mungkin untuk melacak jalan kembali ke sumber kebenaran di tingkat atas.
sumber
Saya telah melakukan ini baru-baru ini di F # dan saya akhirnya menggunakan daftar satu dimensi (dalam F #, itu adalah daftar yang terhubung tunggal). Dalam praktiknya, kecepatan pengindeks daftar O (n) bukanlah hambatan untuk ukuran papan yang dapat digunakan manusia. Saya bereksperimen dengan tipe lain seperti array 2d, tetapi pada akhirnya, itu merupakan trade-off dari penulisan kode pengecekan nilai saya sendiri atau terjemahan dari peringkat dan file untuk diindeks dan dikembalikan. Yang terakhir lebih sederhana. Saya akan mengatakan membuatnya bekerja lebih dulu, dan kemudian mengoptimalkan tipe data Anda nanti jika diperlukan. Sepertinya tidak akan membuat perbedaan yang cukup besar menjadi masalah.
Pada akhirnya implementasi Anda tidak akan menjadi masalah selama operasi dewan Anda dienkapsulasi dengan benar berdasarkan jenis dan operasi Dewan. Sebagai contoh, berikut adalah bagaimana beberapa tes saya terlihat mengatur papan:
Untuk kode ini (atau kode panggilan apa pun), tidak masalah bagaimana dewan direpresentasikan. Dewan direpresentasikan oleh operasi di atasnya lebih dari struktur yang mendasarinya.
sumber