Làm cách nào để tránh các vệt quá may mắn / không may mắn trong thế hệ số ngẫu nhiên?


30

Tôi hiện đang đối phó với một hệ thống chiến đấu nhiều người chơi trong đó sát thương của người chơi luôn được nhân với hệ số ngẫu nhiên trong khoảng từ 0,8 đến 1,2.

Về lý thuyết, một RNG thực sự ngẫu nhiên cuối cùng có thể mang lại cùng một số nhiều lần (xem tình huống khó xử Tetris ). Điều này có thể dẫn đến một trận đấu mà người chơi luôn gây sát thương rất cao trong khi người kia luôn gây sát thương rất thấp.

Tôi có thể làm gì để đảm bảo điều này không xảy ra? Là một số RNG tốt hơn so với những người khác trong việc tránh lặp lại?


Tôi không thấy cách này hoạt động. Tất nhiên, bạn sẽ nhận được một chuỗi x1, x2, x3, x4 .. trong đó tất cả x đều lớn. Không phải đó chỉ là ngẫu nhiên sao?
Vịt Cộng sản

Câu trả lời:


26

Bạn có thể giải quyết nó giống như cách Tetris làm, bằng cách lập danh sách các kết quả thiệt hại và xáo trộn.

Giả sử bạn biết rằng người chơi sẽ gây sát thương từ 0,8 đến 1,2 lần với phân phối tuyến tính. Lấy danh sách [0.8, 0.9, 1.0, 1.1, 1.2]. Xáo trộn ngẫu nhiên , do đó bạn nhận được, ví dụ [1.2, 1.0, 0.8, 0.9, 1.1].

Lần đầu tiên người chơi gây sát thương, họ gây sát thương 1,2 lần. Sau đó 1x. Sau đó, vv, đến 1.1x. Chỉ khi mảng trống, bạn mới tạo và xáo trộn một mảng mới.

Trong thực tế, có thể bạn sẽ muốn làm điều này với hơn 4 mảng cùng một lúc (ví dụ: bắt đầu bằng [0.8,0.8,0.8,0.8,0.9,0.9,0.9,0.9, ...]). Mặt khác, thời gian của chuỗi đủ thấp để người chơi có thể biết liệu lần đánh tiếp theo của họ có "tốt" hay không. (Mặc dù điều đó cũng có thể thêm nhiều chiến lược hơn vào trận chiến, như trong bảng Hoimi của Dragon Quest IX , người ta đã tìm ra cách thăm dò bằng cách nhìn vào các con số hồi phục và điều chỉnh cho đến khi bạn được đảm bảo giảm một lần hiếm.)


3
Để làm cho nó ngẫu nhiên hơn một chút, bạn luôn có thể có một nửa danh sách dưới dạng số ngẫu nhiên và nửa còn lại được tính là (2-x) để lấy trung bình chính xác.
Adam

2
@Adam: Phương pháp đó thực sự chỉ hoạt động đối với ví dụ cụ thể này; nếu bạn đang xử lý các mảnh Tetris chứ không phải là bội số sát thương, khối 2 - S là gì?

6
Thuật ngữ thông thường cho việc này là loại hệ thống là "ngẫu nhiên mà không thay thế". Nó thực sự giống với việc sử dụng một cỗ bài thay vì súc sắc, thực sự.
Kylotan

Thậm chí tốt hơn, bạn có thể thực hiện một nửa số thực sự ngẫu nhiên và chỉ một nửa trong số chúng tuân theo quy tắc này.
o0 '.

1
Nó vẫn có thể dẫn đến phân phối cục bộ không giống với phân phối toàn cầu, đó chính xác là những gì câu hỏi không muốn. Các thuật ngữ như "thực sự ngẫu nhiên" là giả ngẫu nhiên mơ hồ; bạn càng xác định những thuộc tính thống kê nào bạn muốn, ý định và thiết kế trò chơi của bạn sẽ càng rõ ràng.

5

Tôi thực sự đã viết một số mã để làm điều này . Ý chính của nó là sử dụng số liệu thống kê để sửa các vệt không may mắn. Cách bạn có thể làm là theo dõi số lần sự kiện đã xảy ra và sử dụng điều đó để thiên vị số do PRNG tạo ra.

Thứ nhất, làm thế nào để chúng tôi theo dõi tỷ lệ phần trăm của các sự kiện? Cách làm ngây thơ này sẽ là giữ cho tất cả các số được tạo ra trong bộ nhớ và lấy trung bình chúng ra: chúng sẽ hoạt động nhưng không hiệu quả khủng khiếp. Sau một chút suy nghĩ, tôi đã đưa ra những điều sau đây (về cơ bản là trung bình di chuyển tích lũy ).

Lấy các mẫu PRNG sau (trong đó chúng tôi chọn nếu mẫu> = 0,5):

Values: 0.1, 0.5, 0.9, 0.4, 0.8
Events: 0  , 1  , 1  , 0  , 1
Percentage: 60%

Lưu ý rằng mỗi giá trị đóng góp vào 1/5 kết quả cuối cùng. Hãy nhìn nó theo một cách khác:

Values: 0.1, 0.5
Events: 0  , 1

Lưu ý rằng 0đóng góp tới 50% giá trị và 1đóng góp 50% giá trị. Đưa thêm một chút:

Values: [0.1, 0.5], 0.9
Events: [0  , 1  ], 1

Bây giờ các giá trị đầu tiên đóng góp 66% giá trị và 33% cuối cùng. Về cơ bản chúng ta có thể chắt lọc điều này theo quy trình sau:

result = // 0 or 1 depending on the result of the event that was just generated
new_samples = samples + 1

average = (average * samples / new_samples) + (result * 1 / new_samples)
// Essentially:
average = (average * samples / new_samples) + (result / new_samples)

// You might want to limit this to, say, 100.
// Leaving it to carry on increasing can lead to unfairness
// if the game draws on forever.
samples = new_samples

Bây giờ chúng ta cần thiên vị kết quả của giá trị được lấy mẫu từ PRNG, bởi vì chúng ta sẽ có cơ hội tỷ lệ phần trăm ở đây mọi thứ dễ dàng hơn nhiều (so với, giả sử, lượng thiệt hại ngẫu nhiên trong RTS). Điều này sẽ khó giải thích vì nó 'chỉ xảy ra với tôi'. Nếu mức trung bình thấp hơn có nghĩa là chúng ta cần tăng cơ hội xảy ra sự kiện và ngược lại. Vì vậy, một số ví dụ

average = 0.1
desired = 0.5
corrected_chance = 83%

average = 0.2
desired = 0.5
corrected_chance = 71%

average = 0.5
desired = 0.5
corrected_change = 50%

Bây giờ, điều 'xảy ra với tôi' là trong ví dụ đầu tiên, 83% chỉ là "0,5 trên 0,6" (nói cách khác là "0,5 trên 0,5 cộng với 0,1"). Trong thuật ngữ sự kiện ngẫu nhiên có nghĩa là:

procced = (sample * 0.6) > 0.1
// or
procced = (sample * 0.6) <= 0.5

Vì vậy, để tạo sự kiện, về cơ bản bạn sẽ sử dụng đoạn mã sau:

total = average + desired
sample = rng_sample() * total // where the RNG provides a value between 0 and 1
procced = sample <= desired

Và do đó, bạn nhận được mã mà tôi đặt trong ý chính. Tôi khá chắc chắn rằng tất cả có thể được sử dụng trong trường hợp thiệt hại ngẫu nhiên, nhưng tôi đã không dành thời gian để tìm ra điều đó.

Tuyên bố từ chối trách nhiệm: Đây là tất cả các số liệu thống kê được trồng tại nhà, tôi không có giáo dục trong lĩnh vực này. Bài kiểm tra đơn vị của tôi làm vượt qua mặc dù.


Trông giống như một lỗi trong ví dụ đầu tiên của bạn vì cả giá trị 0,1 và 0,9 đều dẫn đến một sự kiện 0. Nhưng về cơ bản, bạn đang mô tả việc giữ mức trung bình di chuyển tích lũy ( en.wikipedia.org/wiki/Moving_aenses#Cumulators_moving_alusive ) và sửa lỗi dựa trên điều đó. Một rủi ro là mỗi kết quả sẽ tương quan nghịch với kết quả trước đó, mặc dù mối tương quan này sẽ giảm theo thời gian.
Kylotan

1
Thay vào đó, tôi sẽ thay đổi điều này để sử dụng hệ thống 'tích hợp rò rỉ': bắt đầu với mức trung bình được khởi tạo là 0,5 và thay vì đếm các mẫu chọn giá trị hằng số tùy ý (ví dụ: 10, 20, 50 hoặc 100) không được tăng . Sau đó, ít nhất là mối tương quan giữa 2 giá trị tiếp theo là không đổi trong suốt quá trình sử dụng trình tạo. Bạn cũng có thể điều chỉnh giá trị không đổi - giá trị lớn hơn có nghĩa là hiệu chỉnh chậm hơn và tính ngẫu nhiên rõ ràng hơn.
Kylotan

@Kylotan cảm ơn, cảm ơn vì đã cung cấp tên. Tôi không chắc chính xác ý bạn là gì với bình luận thứ hai của bạn - có thể cung cấp câu trả lời mới?
Jonathan Dickinson

Điều đó khá thông minh và không có giới hạn của mảng. Tôi hiểu đề xuất của Kylotan, đó là khởi tạo samplesở giá trị tối đa của nó (trong trường hợp này là 100) từ đầu. Theo cách đó, không cần 99 lần lặp để RNG ổn định. Dù bằng cách nào, một nhược điểm tôi có thể thấy với phương pháp này là nó không đảm bảo tính công bằng, nó chỉ đơn giản là đảm bảo mức trung bình không đổi.
Người dùng không tìm thấy

@jSepia - thực sự, bạn vẫn sẽ có được những bước chạy công bằng / không công bằng nhưng chúng sẽ được theo dõi (thường là) bằng cách chạy cân bằng. Vd Trong các tình huống không được xác định (nếu bạn nhìn vào mã), 50% Proc thường thấy, tệ nhất là, chạy 2/3 theo một trong hai hướng. Nhưng một người chơi có thể có một cuộc chạy cho phép họ đánh bại người chơi khác. Nếu bạn muốn thiên vị nó mạnh mẽ hơn để công bằng : total = (average / 2) + desired.
Jonathan Dickinson

3

Những gì bạn đang yêu cầu thực sự trái ngược với hầu hết các PRNG, một phân phối phi tuyến tính. Chỉ cần đưa vào một số loại logic trả về giảm dần trong các quy tắc của bạn, Giả sử rằng mọi thứ trên 1.0x là một "cú đánh chí mạng" nào đó, chỉ cần nói rằng mỗi vòng bạn sẽ có cơ hội nhận được một lời phê bình, cho đến khi bạn đạt được Điểm nào họ đặt lại thành Y. Sau đó, bạn thực hiện hai cuộn mỗi vòng, một để xác định crit hay không, và sau đó một điểm khác cho độ lớn thực tế.


1
Đây là cách tiếp cận chung mà tôi thực hiện, bạn sử dụng phân phối RNG thống nhất nhưng biến đổi nó. Bạn cũng có thể sử dụng đầu ra của RNG làm đầu vào cho phân phối tùy chỉnh của riêng bạn, nó tự điều chỉnh lại dựa trên lịch sử gần đây, nghĩa là buộc phương sai trong các đầu ra, để nó trông "ngẫu nhiên hơn" trong các thuật ngữ nhận thức của con người.
Michael

3
Tôi thực sự biết về một MMO làm một cái gì đó như thế này, nhưng cơ hội của một crit thực sự tăng lên mỗi khi bạn nhận được một cho đến khi bạn không nhận được nó, sau đó nó đặt lại với giá trị rất thấp. Điều này dẫn đến những vệt crits hiếm hoi rất thỏa mãn người chơi.
coderanger

Âm thanh như một alg tốt, những câu thần chú khô dài luôn luôn gây nản lòng, nhưng nó không dẫn đến những vệt điên cuồng.
Michael

2
Khắc phục điều này không yêu cầu phân phối phi tuyến, nó chỉ yêu cầu các tập hợp con tuần tự thời gian ngắn của phân phối có các thuộc tính giống như bản phân phối.

đây là cách các trò chơi Blizzard làm điều đó, ít nhất kể từ Warcraft 3
dreta

2

Sid Meier đã có một bài phát biểu xuất sắc trên GDC 2010 về chủ đề này và các trò chơi Civilization. Tôi sẽ cố gắng tìm và dán liên kết sau. Về bản chất - ngẫu nhiên nhận thức không giống như ngẫu nhiên thực sự. Để làm cho mọi thứ cảm thấy công bằng, bạn cần phân tích kết quả trước đó và chú ý đến tâm lý của người chơi.

Tránh những vệt xui xẻo bằng mọi giá (nếu hai lượt trước không may mắn thì lần sau sẽ được đảm bảo là may mắn). Người chơi phải luôn may mắn hơn đối thủ AI.


0

Sử dụng xu hướng thay đổi

01rb0

Phân phối tổng thể sẽ được thiên vị theo công thức sau:

rđiểm kinh nghiệm(-b)

b1b0

Lấy số này và chia tỷ lệ thích hợp cho phạm vi mong muốn.

Mỗi khi người chơi cuộn thuận lợi, trừ đi sự thiên vị. Mỗi lần người chơi lăn bất lợi, hãy thêm vào sự thiên vị. Số lượng thay đổi có thể được thu nhỏ bằng cách (un) thuận lợi cho cuộn hoặc có thể là một số lượng bằng phẳng (hoặc kết hợp). Bạn sẽ cần điều chỉnh các giá trị cụ thể để phù hợp với cảm giác bạn đang đi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.