Brainstorm: cara melihat tugas dari sudut yang berbeda

Brainstorming dengan transposisi


Terkadang saya macet dan harus mencari cara untuk memikirkan tugas dari sudut yang berbeda. Ada tugas yang bisa ditampilkan dalam bentuk matriks atau tabel. Struktur mereka terlihat seperti ini:

ABCDE
1A1B1C1D1E1
2A2B2C2D2E2
3A3B3C3D3E3
4A4B4C4D4E4
5A5B5C5D5E5

Sel yang saya kerjakan disusun dalam kolom dan baris. Mari kita ambil contoh dari gim sederhana:

SerangPertahankanSpesial
Petarungpedangbaju besibanting
Magebola apimencerminkanmembeku
Pencuribelatimenghindarmelucuti senjata

Garis adalah kelas karakter: prajurit, penyihir, pencuri.

Kolom adalah jenis tindakan: serangan, pertahanan, tindakan khusus.

Matriks berisi semua kode untuk memproses setiap jenis tindakan untuk setiap jenis karakter.

Seperti apa kode itu? Biasanya, struktur seperti itu disusun dalam modul-modul tersebut:

  1. Fighter akan berisi kode untuk menangani serangan pedang, mengurangi kerusakan dengan baju besi dan pukulan kuat khusus.
  2. Mage akan berisi kode penanganan bola api, refleksi kerusakan dan serangan pembekuan khusus.
  3. Thief akan berisi kode untuk menangani serangan belati, menghindari kerusakan menghindar, dan serangan melucuti senjata khusus.

Terkadang berguna untuk mengubah posisi matriks. Kita bisa mengaturnya di sumbu lain :

PetarungMagePencuri
Serangpedangbola apibelati
Pertahankanbaju besimencerminkanmenghindar
Spesialbantingmembekumelucuti senjata

  1. Attack akan berisi kode untuk menangani serangan ayunan, bola api, dan belati.
  2. Defend akan berisi kode untuk menangani pengurangan kerusakan, mencerminkan kerusakan dan menghindari kerusakan.
  3. Special akan berisi serangan yang kuat, membekukan dan melucuti kode penanganan.

Saya diajari bahwa satu gaya "baik" dan yang lain "buruk". Tetapi tidak jelas bagi saya mengapa semuanya harus seperti itu. Alasannya adalah asumsi bahwa kita akan sering menambahkan kelas karakter baru (kata benda), dan jarang menambahkan jenis tindakan baru (kata kerja). Dengan cara ini saya dapat menambahkan kode menggunakan modul baru tanpa menyentuh semua yang tersedia. Dalam gim Anda, semuanya bisa berbeda. Melihat matriks transposisi, saya sadar akan adanya asumsi dan dapat meragukannya. Kemudian saya akan memikirkan jenis fleksibilitas yang saya butuhkan, dan hanya kemudian saya akan memilih struktur kode.

Mari kita lihat contoh lain.

Interpretasi bahasa pemrograman memiliki berbagai jenis node yang sesuai dengan primitif: konstanta, operator, loop, percabangan, fungsi, jenis, dll. Kita perlu menghasilkan kode untuk semuanya.

Buat kode
Konstan
Operator
Loop
Cabang
Fungsi
Jenis

Hebat! Anda bisa membuat satu kelas untuk setiap jenis node, dan mereka semua bisa mewarisi dari kelas dasar Node . Tetapi kami mengandalkan asumsi bahwa kami akan menambahkan baris dan kolom yang lebih jarang. Apa yang terjadi dalam kompilator pengoptimalan? Kami menambahkan pass optimasi baru. Dan masing-masing dari mereka adalah kolom baru.

Buat kodeAliran dataLipat konstanLoop fusion...
Konstan
Operator
Loop
Cabang
Fungsi
Jenis

Jika saya ingin menambahkan pass optimisasi baru, maka saya perlu menambahkan metode baru untuk setiap kelas, dan semua kode pass optimisasi akan didistribusikan ke modul yang berbeda. Saya ingin menghindari situasi seperti itu! Oleh karena itu, dalam beberapa sistem, lapisan lain ditambahkan di atas ini. Menggunakan pola pengunjung, saya dapat menyimpan semua kode untuk menggabungkan loop dalam satu modul, daripada memecahnya menjadi beberapa file.

Jika Anda melihat matriks transposisi, kami akan membuka pendekatan lain:

KonstanOperatorLoopCabangFungsiJenis
Buat kode
Aliran data
Lipat konstan
SSA
Loop fusion

Sekarang, alih-alih kelas dengan metode, saya dapat menggunakan gabungan tag dan pencocokan pola (mereka tidak didukung dalam semua bahasa pemrograman). Karena ini, seluruh kode dari setiap pass optimasi akan disimpan bersama-sama dan dapat dilakukan tanpa tidak langsung dari pola pengunjung.

Seringkali berguna untuk melihat masalah dari sudut pandang matriks. Jika Anda menerapkannya pada struktur berorientasi objek yang dipikirkan semua orang, itu dapat mengarahkan saya ke sesuatu yang lain, misalnya, ke pola "entitas-komponen-sistem", database relasional, atau pemrograman reaktif.

Dan ini tidak hanya berlaku untuk kode. Ini adalah contoh penerapan ide ini pada produk. Misalkan ada orang dengan minat berbeda:

NickFengSayidAlice
mobilXX
politikXX
matematikaXX
bepergianXX

Jika saya mengembangkan situs jejaring sosial, saya dapat memungkinkan orang untuk mengikuti berita orang lain. Nick dapat mendaftar untuk Alice, karena mereka berdua tertarik pada mobil, dan pada Fenya, karena mereka berdua tertarik bepergian. Tapi Nick juga akan menerima posting Alice tentang matematika dan posting Fenya tentang politik. Jika saya mempertimbangkan matriks yang diubah, saya dapat memungkinkan orang untuk berlangganan topik . Nick bisa bergabung dengan sekelompok pecinta mobil, serta sekelompok pelancong. Facebook dan Reddit dimulai sekitar waktu yang sama, tetapi mereka mengubah matriks satu sama lain. Facebook memungkinkan Anda untuk mengikuti orang; Reddit memungkinkan Anda untuk berlangganan topik.

Ketika saya berhenti atau ketika saya ingin mempertimbangkan alternatif, saya melihat masalah dan mencari kapak pemesanan yang berbeda di dalamnya. Terkadang melihat masalah dari sudut yang berbeda dapat memberikan solusi yang lebih baik.

Brainstorming Dekomposisi


Saya menggunakan teknik lain yang disebut dekomposisi.

Dalam aljabar, operasi dekomposisi mengubah polinomial dari bentuk 5x² + 8x - 21 menjadi (x + 3) · (5x - 7). Untuk menyelesaikan persamaan 5x² + 8x - 21 = 0, pertama-tama kita dapat memasukkannya menjadi (x + 3) · (5x - 7) = 0. Kemudian kita dapat mengatakan bahwa x + 3 = 0 atau 5x - 7 = 0. Ekspansi mengubah tugas yang sulit menjadi beberapa tugas yang lebih mudah.

x3
5x5x²15x
-7-7x-21

Mari kita lihat sebuah contoh: Saya memiliki enam kelas: File , EncryptedFile , GzipFile , EncryptedGzipFile , BzipFile , EncryptedBzipFile . Saya bisa menguraikannya menjadi sebuah matriks:

Tidak terkompresiGzipBzip
Tidak terenkripsiFileGzip (file)Bzip (file)
DienkripsiEnkripsi (File)Enkripsi (Gzip (File))Enkripsi (Bzip (File))

Menggunakan pola "dekorator" (atau pengotor), saya mengubah enam jenis file menjadi empat komponen: polos, gzip, bzip, terenkripsi. Ini sepertinya tidak banyak menghemat, tetapi jika saya menambahkan lebih banyak variasi, penghematan akan meningkat. Dekomposisi mengubah komponen O (M * N) menjadi komponen O (M + N).

Contoh lain: kadang-kadang orang bertanya kepada saya pertanyaan seperti "bagaimana cara menulis interpolasi linier dalam C #?" . Saya dapat menulis banyak tutorial potensial:

C ++PythonJawaC #JavascriptKaratIdris...
Interpolasi
Tetangga
Merintis jalan
Jarak
Peta sungai
Isometrik
Voronoi
Transformasi
...

Jika ada topik M dan bahasa N, maka saya bisa menulis tutorial M * N. Namun, ini banyak pekerjaan. Sebagai gantinya, saya akan menulis tutorial tentang interpolasi , orang lain akan menulis tutorial tentang C #, dan kemudian pembaca akan menggabungkan pengetahuan C # dengan pengetahuan interpolasi, dan menulis versinya tentang interpolasi di C #.

Seperti transposisi, dekomposisi tidak selalu membantu, tetapi jika berlaku, itu bisa sangat berguna.

Brainstorming mundur


Dalam dua bagian sebelumnya, saya berbicara tentang bagaimana saya kadang-kadang mendekati tugas, mencoba mengaturnya menjadi sebuah matriks. Terkadang ini tidak membantu dan kemudian saya mencoba melihat tugas dengan arah yang berlawanan. Mari kita lihat pembuatan peta prosedural, misalnya. Seringkali saya mulai dengan fungsi noise, kemudian menambahkan oktaf, menyesuaikan parameter, dan menambahkan layer. Saya melakukan ini karena saya memerlukan kartu yang memiliki properti tertentu.


Sangat mungkin untuk memulai dengan percobaan dengan parameter, tetapi ruang parameternya cukup besar, dan tidak diketahui apakah saya akan menemukan parameter yang paling cocok untuk kebutuhan saya. Karena itu, setelah bereksperimen sedikit, saya berhenti dan mulai berpikir dalam urutan terbalik: jika saya bisa menggambarkan apa yang saya butuhkan, maka ini dapat membantu dalam menemukan parameter.

Motivasi inilah yang membuat saya belajar aljabar. Jika kita memiliki persamaan bentuk 5x² + 8x - 21 = 0 , lalu apa yang akan menjadi x ? Ketika saya tidak tahu aljabar, saya akan menyelesaikan persamaan ini, mencoba untuk mengganti nilai-nilai berbeda dari x , pertama-tama memilihnya secara acak, dan kemudian menyesuaikannya ketika saya merasa bahwa saya sudah dekat dengan solusi. Aljabar memberi kita alat untuk pergi ke arah yang berbeda. Alih-alih menebak jawabannya, dia memberi saya alat (dekomposisi, atau persamaan kuadrat, atau metode Newton untuk pencarian iteratif untuk root), yang saya dapat lebih sadar gunakan untuk mencari nilai x (-3 atau 7/5).

Saya merasa seperti saya sering masuk ke dalam situasi pemrograman semacam ini. Saat mengerjakan pembuatan peta prosedural, setelah bereksperimen dengan parameter untuk beberapa waktu, saya berhenti dan menyusun daftar apa yang seharusnya ada di dunia game dari satu proyek :

  1. Pemain harus memulai permainan jauh dari pantai.
  2. Saat naik level, pemain harus naik ke atas bukit.
  3. Pemain seharusnya tidak dapat mencapai tepi peta.
  4. Ketika level bertambah, pemain harus bergabung dalam grup.
  5. Seharusnya ada monster sederhana di pantai tanpa banyak variasi.
  6. Di dataran harus ada berbagai monster dengan kesulitan sedang.
  7. Di daerah pegunungan pasti ada bos monster yang kompleks.
  8. Pasti ada semacam tengara yang memungkinkan pemain tetap pada tingkat kesulitan yang sama, dan tengara lain yang memungkinkan Anda naik atau turun di tingkat kesulitan.

Kompilasi daftar ini mengarah pada pembuatan pembatasan berikut:

  1. Dunia permainan haruslah pulau-pulau dengan banyak pantai dan puncak kecil di tengahnya.
  2. Ketinggian harus sesuai dengan kompleksitas monster.
  3. Pada ketinggian rendah dan tinggi, seharusnya ada lebih sedikit variabilitas bioma daripada di ketinggian sedang.
  4. Jalan harus tetap pada tingkat kesulitan yang sama.
  5. Sungai harus mengalir dari ketinggian besar ke kecil, dan memberi pemain kemampuan untuk naik / turun.

Keterbatasan ini membuat saya merancang generator peta. Dan dia menghasilkan kartu yang jauh lebih baik daripada yang saya terima dengan menyesuaikan parameter, seperti yang biasa saya lakukan. Dan artikel yang dihasilkan menarik banyak orang untuk membuat peta berdasarkan diagram Voronoi.

Tes unit adalah contoh lain. Disarankan agar saya membuat daftar contoh untuk diuji. Sebagai contoh, untuk kisi-kisi segi enam, saya mungkin berpikir bahwa saya perlu memeriksa kondisi add(Hex(1, 2), Hex(3, 4)) == Hex(4, 6) . Maka saya dapat mengingat bahwa Anda perlu memeriksa nol: add(Hex(0, 1), Hex(7, 9)) == Hex(7, 10) . Maka saya dapat mengingat bahwa Anda perlu memeriksa nilai negatif: add(Hex(-3, 4) + Hex(7, -8)) == Hex(4, -4) . Bagus, bagus, saya punya beberapa unit test.

Tetapi jika Anda berpikir sedikit lebih jauh, maka sebenarnya saya periksa add(Hex(A, B), Hex(C, D)) == Hex(A+C, B+D) . Saya datang dengan tiga contoh yang ditunjukkan di atas berdasarkan aturan umum ini. Saya akan ke arah yang berlawanan dari aturan ini untuk datang ke unit test. Jika saya bisa langsung menyandikan aturan ini ke dalam sistem pengujian, maka sistem itu sendiri akan dapat bekerja dalam urutan terbalik untuk membuat contoh untuk pengujian. Ini disebut pengujian berbasis properti. (Lihat juga: pengujian metamorf )

Contoh lain: pemecah kendala. Dalam sistem seperti itu, pengguna menggambarkan apa yang ingin dilihat dalam output, dan sistem menemukan cara untuk memenuhi kendala ini. Kutipan dari Buku Generasi Konten Prosedural, bab 8 :

Dengan menggunakan metode konstruktif dari Bab 3, serta metode fraktal dan noise dari Bab 4, kita dapat membuat berbagai jenis data keluaran dengan menyetel algoritme hingga kami puas dengan data keluarannya. Tetapi jika kita tahu properti apa yang seharusnya dimiliki oleh konten yang dihasilkan, akan lebih mudah untuk menunjukkan secara langsung apa yang kita inginkan dari algoritma umum untuk menemukan konten yang memenuhi kriteria kita.

Buku ini menjelaskan pemrograman set jawaban (ASP), di mana kami menggambarkan struktur dari apa yang kami kerjakan (ubin adalah lantai dan dinding, ubin saling membatasi), struktur solusi yang kami cari (ruang bawah tanah adalah grup ubin terhubung dengan awal dan akhir) dan sifat-sifat solusi (lorong samping harus berisi tidak lebih dari 5 kamar, harus ada 1-2 loop di labirin, tiga asisten harus dikalahkan sebelum mencapai bos). Setelah itu, sistem menciptakan solusi yang memungkinkan dan memungkinkan Anda memutuskan apa yang harus dilakukan dengannya.

Baru-baru ini, pemecah kendala dikembangkan, yang menyebabkan minat besar karena namanya yang keren dan demo yang menarik: Wave Function Collapse. [Ada artikel tentang pemecah masalah ini pada Habr.] Jika Anda memberinya contoh gambar untuk menunjukkan batasan apa yang dikenakan pada ubin tetangga, ia akan membuat contoh baru yang sesuai dengan pola yang diberikan. Karyanya dijelaskan dalam WaveFunctionCollapse adalah Constraint Solving in the Wild :

WFC mengimplementasikan metode pencarian yang serakah tanpa kembali. Artikel ini mengeksplorasi WFC sebagai contoh metode keputusan berbasis kendala.

Saya telah mencapai banyak hal dengan bantuan pemecah kendala. Seperti halnya aljabar, sebelum saya belajar cara menggunakannya secara efektif, saya perlu belajar banyak.

Contoh lain: pesawat ruang angkasa yang saya buat . Pemain dapat menyeret mesin ke mana saja, dan sistem akan menentukan mesin mana yang perlu diaktifkan ketika Anda mengklik W, A, S, D, Q, E. Misalnya, di kapal ini:


Jika Anda ingin terbang ke depan, maka sertakan dua mesin belakang. Jika Anda ingin berbelok ke kiri, maka hidupkan mesin kanan belakang dan kiri depan. Saya mencoba mencari solusi, memaksa sistem untuk beralih pada banyak parameter :


Sistem bekerja, tetapi tidak sempurna. Kemudian saya menyadari bahwa ini adalah contoh lain di mana solusi dalam arah yang berlawanan dapat membantu. Ternyata pergerakan pesawat ruang angkasa dapat digambarkan dengan sistem pembatasan linear . Jika saya memahami hal ini, saya bisa menggunakan pustaka siap pakai yang secara akurat menyelesaikan kendala, dan bukan metode coba-coba saya, yang mengembalikan perkiraan.

Dan contoh lain: proyek G9.js, di mana Anda dapat menyeret output fungsi tertentu di layar, dan menentukan bagaimana mengubah data input agar sesuai dengan data output yang diinginkan. Demo G9.js tampak hebat! Pastikan untuk menghapus tanda komentar pada baris "hapus komentar baris berikut" di demo Rings.

Terkadang berguna untuk memikirkan tugas dalam urutan terbalik. Sering kali ternyata ini memberi saya solusi yang lebih baik daripada dengan alasan langsung.

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


All Articles