Bagaimana saya menghasilkan acak "menyenangkan", yang bertentangan dengan pseudo-acak?

26

Saya membuat game yang menghadirkan sejumlah jenis puzzle secara berurutan. Saya memilih setiap teka-teki dengan nomor pseudorandom. Untuk setiap teka-teki, ada sejumlah variasi. Saya memilih variasi dengan nomor pseudorandom lain. Dan seterusnya.

Masalahnya adalah, sementara ini menghasilkan keacakan hampir-benar, ini bukan apa yang benar-benar diinginkan pemain. Pemain biasanya menginginkan apa yang mereka anggap sebagai dan mengidentifikasi secara acak, tetapi hanya jika tidak cenderung mengulangi teka-teki. Jadi, tidak terlalu acak. Tidak dapat diprediksi.

Setelah memikirkannya, saya bisa membayangkan cara-cara aneh melakukannya. Misalnya, untuk sementara waktu menghilangkan pilihan N terbaru dari serangkaian kemungkinan saat memilih pilihan baru. Atau menetapkan setiap pilihan dengan probabilitas yang sama, mengurangi probabilitas pilihan menjadi nol pada pilihan, dan kemudian meningkatkan semua probabilitas secara perlahan dengan setiap pilihan.

Saya berasumsi ada cara yang mapan untuk melakukan ini, tetapi saya tidak tahu terminologi sehingga saya tidak dapat menemukannya. Adakah yang tahu Atau adakah yang memecahkan ini dengan cara yang menyenangkan?

Hilton Campbell
sumber
4
Buku "AI Game Programming Wisdom 2" memiliki bab tentang keacakan yang disaring yang, dari apa yang saya ingat, cukup banyak persis apa yang Anda cari. Saya tidak memilikinya saat ini jadi saya tidak bisa memberikan jawaban penuh.
Anton
Untuk mencoba dan mengklarifikasi: ketika Anda mengatakan 'tidak mengulangi teka-teki', maksud Anda, Anda tidak ingin dua teka-teki dengan tipe yang sama bersebelahan? Jadi dengan kata lain, jika Anda baru saja memilih sudoku, jangan menawarkan puzzle sudoku lain, tetapi jika itu Sudoku # 19, maka boleh saja menawarkan Picross # 19 berikutnya (dengan kata lain, nomor variasi tidak masalah) ?
Steven Stadnicki
2
Pertanyaan yang sangat terkait: stackoverflow.com/questions/910215/…
Chris Burt-Brown
1
Oke, salinan AI Game Programming Wisdom 2 saya baru saja tiba. Saya membaca bab tentang keacakan yang difilter dan memeriksa kode sumber. Ini mungkin pendekatan terbaik. Ini memungkinkan saya untuk hanya menggunakan angka acak, tetapi kemudian menyaring angka sehingga pola yang tidak terduga tidak terjadi. Tampaknya lebih banyak bukti daripada tas shuffle.
Hilton Campbell
1
Namun pembaruan lain ... untuk aplikasi khusus saya, keacakan disaring tidak cukup melakukannya. Saya benar-benar ingin pemain bermain melalui semua jenis dan subtipe sebelum diulang, jadi saya pergi dengan tas acak.
Hilton Campbell

Jawaban:

25

Jika Anda memiliki jumlah teka-teki yang terbatas, Anda dapat:

  • Buat daftar teka-teki, baik semuanya atau beberapa dipilih secara acak;
  • Kocok daftar ini (lihat Knuffle Shuffle misalnya);
  • Biarkan pemain Anda bermain melalui daftar ini;
  • Ketika daftar kosong, mulailah dengan yang baru.

EDIT

Saya tidak tahu ini, tetapi browsing SE membuat saya menyadari bahwa ini sebenarnya dikenal sebagai "tas acak". Beberapa info lebih lanjut di sini , di sini atau di sana .

EDIT 2

Knuth Shuffle klasik berjalan seperti ini:

To shuffle an array a of n elements (indices 0..n-1):
    for i from n  1 down to 1 do
        j  random integer with 0  j  i
        exchange a[j] and a[i]

Steven Stadnicki dengan tepat menunjukkan dalam komentarnya bahwa hal semacam ini tidak mencegah pengulangan pada perombakan. Cara untuk memperhitungkan ini adalah dengan menambahkan case khusus untuk item terakhir:

To reshuffle an array a of n elements and prevent repetitions (indices 0..n-1):
    return if n <= 2

    // Classic Knuth Shuffle for all items *except* the last one
    for i from n  2 down to 1 do
        j  random integer with 0  j  i
        exchange a[j] and a[i]

    // Special case for the last item
    // Exchange it with an item which is *not* the first one
    r  random integer with 1  r  n - 1
    exchange a[r] and a[n - 1]
Laurent Couvidou
sumber
1
Ini bisa berhasil, tetapi Anda harus sedikit berhati-hati jika melakukan perombakan setelah setiap kali bermain untuk tidak memulai dengan item yang sama dengan yang Anda berakhir.
Steven Stadnicki
@ Sebelas Memang. Item ini dapat dikecualikan dari daftar baru. Sebenarnya, itu bisa menjadi ide untuk membangun daftar beberapa item acak saja, dan membangun daftar berikutnya dengan hanya item yang tersisa. Jadi, jika Anda memiliki 100 item, buat misalnya 10 daftar. Setelah selesai dengan daftar ini, buat yang berikutnya dengan 10 item dalam 90 yang tidak dipilih sebelumnya.
Laurent Couvidou
+1. Dukungan tambahan untuk teknik ini menjadi "lebih menyenangkan": ini adalah bagaimana Tetris, misalnya, menghasilkan potongan "acak". Seperangkat dari masing-masing bagian dikocok dan diulangi, yang menghindari urutan duplikat panjang yang akan dihasilkan oleh keacakan yang benar.
KutuluMike
1
@Hilton Saya cenderung tidak suka pendekatan "sementara ini sampai acak memberi saya apa yang saya inginkan" ... Tidak mungkin ini akan menyebabkan masalah. Tapi tetap saja, saya selalu merasa seperti ini adalah panggilan untuk loop tak terbatas acak atau penurunan kinerja - yang mengerikan untuk debug. Dengan mengecualikan item terakhir dari daftar sebelumnya dari yang baru, Anda hanya dapat mengacak satu kali, untuk hasil yang sama.
Laurent Couvidou
1
Anda benar, dan saya memiliki reservasi yang sama. Daripada mengecualikan item terakhir sebelumnya, saya sekarang memilikinya hanya mengocok sekali, dan kemudian jika item terakhir sebelumnya adalah yang pertama, saya menukar dengan item lain secara acak.
Hilton Campbell
2

Varian pada pendekatan lorancou: untuk setiap jenis puzzle, simpan satu array nomor puzzle (dikocok); lalu setiap kali Anda menekan puzzle jenis itu, dapatkan nomor berikutnya dari daftar. misalnya, misalkan Anda memiliki teka-teki Sudoku, Picross, dan Kenken, masing-masing dengan teka-teki # 1..6. Anda akan membuat tiga array acak dari angka 1..6, satu untuk setiap jenis puzzle:

  • Sudoku: [5, 6, 1, 3, 4, 2]
  • Picross: [6, 2, 4, 1, 3, 5]
  • KenKen: [3, 2, 5, 6, 4, 1]

Sekarang, Anda akan mengocok jenis puzzle seperti yang disarankan lorancu; katakanlah itu muncul [Picross, Sudoku, Kenken]. Kemudian setiap kali Anda menekan puzzle dari jenis yang diberikan, gunakan angka berikutnya dalam 'daftar acak'; keseluruhan presentasi puzzle Anda adalah [Sudoku # 5, Picross # 6, Kenken # 3, Sudoku # 6, Picross # 2, Kenken # 2, ...]

Jika Anda tidak ingin menyimpan puzzle dalam urutan keseluruhan yang sama setiap kali melalui loop, maka saya pikir Anda memilih secara acak, mengabaikan pilihan picks terakhir adalah yang terbaik. Ada beberapa cara Anda dapat membuat ini sedikit lebih efisien juga; misalnya, katakanlah Anda memiliki 20 hal dan Anda ingin mengabaikan 5 pilihan terakhir. Kemudian alih-alih secara acak memilih angka 1..20 dan 'rerolling' sampai Anda mendapatkan satu di luar 5 terakhir, alih-alih hanya memilih nomor 1..15 dan berjalan melalui jenis puzzle Anda yang banyak langkah, hanya melompati setiap jenis puzzle yang telah dipilih (Anda dapat melakukan ini dengan mudah dengan menjaga sedikit array yang menampung 5 teka-teki yang dipilih terakhir).

Steven Stadnicki
sumber