Sisteminizi bir dizi durum ve işlevden oluşuyormuş gibi görüntüleyebilirsiniz f[j]
; girişli bir işlev x[j]
sistem durumunu duruma s[j]
dönüştürür s[j+1]
, şöyle:
s[j+1] = f[j](s[j], x[j])
Bir devlet, tüm dünyanızın açıklamasıdır. Oyuncunun yerleri, düşmanın yeri, skor, kalan cephane vb. Oyununun bir çerçevesini çizmek için gereken her şey.
Bir fonksiyon dünyayı etkileyebilecek herhangi bir şeydir. Bir çerçeve değişikliği, bir tuşa basma, bir ağ paketi.
Giriş, işlevin aldığı verilerdir. Bir kare değişikliği, son karenin geçmesinden bu yana geçen süreyi alabilir, tuşa basılması, basılan tuşun yanı sıra, üst karakter tuşuna basılıp basılmayacağını da içerebilir.
Bu açıklama uğruna aşağıdaki varsayımları yapacağım:
Varsayım 1:
Oyunun belirli bir çalışması için durum miktarı, fonksiyonların miktarından çok daha fazladır. Muhtemelen yüz binlerce devlete sahipsiniz, ancak sadece birkaç düzine işlev (çerçeve değişikliği, tuşa basma, ağ paketi, vb.). Tabii ki, girdilerin miktarı eksi bir devletlerin miktarına eşit olmalıdır.
Varsayım 2:
Tek bir durumu kaydetmenin uzaysal maliyeti (bellek, disk), bir işlevin ve bunun girişinin kaydedilmesinden çok daha büyüktür.
Varsayım 3:
Bir devlet sunmanın zamansal maliyeti (zamanı) benzerdir ya da bir devlet üzerindeki bir işlevi hesaplamaktan yalnızca bir ya da iki büyüklük emri alır.
Yeniden oynatma sisteminizin gereksinimlerine bağlı olarak, bir yeniden oynatma sistemini uygulamanın birkaç yolu vardır, böylece en basitinden başlayalım. Ayrıca kağıda kaydedilmiş olan satranç oyununu kullanarak küçük bir örnek yapacağım.
Yöntem 1:
Mağaza s[0]...s[n]
. Bu çok basit, çok basit. 2. varsayım nedeniyle, bunun uzaysal maliyeti oldukça yüksektir.
Satranç için, bu her hamle için bütün tahtayı çizerek başarılabilirdi.
Yöntem 2:
Yalnızca ileriye tekrar oynatmaya ihtiyacınız varsa, basitçe saklayabilir s[0]
ve sonra saklayabilirsiniz f[0]...f[n-1]
(unutmayın, bu sadece işlevin kimliği olan isimdir) ve x[0]...x[n-1]
(bu işlevlerin her birinin girişi neydi). Tekrarlamak için, basitçe başlamak s[0]
ve hesaplamak
s[1] = f[0](s[0], x[0])
s[2] = f[1](s[1], x[1])
ve bunun gibi...
Burada küçük bir açıklama yapmak istiyorum. Diğer bazı yorumcular oyunun "deterministik" olması gerektiğini söyledi. Bunun Computer Computer 101'i tekrar alması gerektiğini söyleyen herkes, çünkü oyununuz kuantum bilgisayarlarda çalıştırılmak istenmediği sürece, TÜM BİLGİSAYAR PROGRAMLARI TESPİT EDER. Bilgisayarları bu kadar harika yapan da bu.
Ancak, programınız büyük olasılıkla kütüphanelerden işlemcinin fiili uygulamasına kadar uzanan harici programlara bağlı olduğundan, işlevlerinizin platformlar arasında aynı şekilde davranmasını sağlamak oldukça zor olabilir.
Sözde rasgele sayılar kullanırsanız, oluşturulan sayıları girişinizin bir parçası olarak x
veya prng işlevinin durumunu durumunuzun bir parçası olarak s
ve bunun işlevinin bir parçası olarak uygulayabilirsiniz f
.
Satranç için bu, ilk tahtanın (bilinen) çizilmesi ve sonra hangi parçanın nereye gittiğini söyleyerek her hareketi açıklamakla başarılabilir. Bu arada, bunu gerçekten yapıyorlar.
Yöntem 3:
Şimdi, büyük olasılıkla tekrarınızı arayabilmek isteyeceksiniz. Yani, s[n]
keyfi bir şekilde hesaplayın n
. Yöntem 2'yi kullanarak, hesaplama s[0]...s[n-1]
yapmadan önce hesaplamanız gerekir s[n]
, bu varsayım 2'ye göre oldukça yavaş olabilir.
Bu uygulamak için, yöntem 3 yöntem 1 ve 2 arasında bir genellemedir: mağaza f[0]...f[n-1]
ve x[0]...x[n-1]
sadece yöntem 2 gibi, ama aynı zamanda depolamak s[j]
için tüm, j % Q == 0
sabit verilen için Q
. Daha kolay bir ifadeyle, bu, bir yer işaretini her Q
eyaletten birinde sakladığınız anlamına gelir . Örneğin Q == 100
, sizin içins[0], s[100], s[200]...
Hesaplayabilmek için s[n]
keyfi bir için n
, önce önceden depolanan yük s[floor(n/Q)]
ve ardından gelen tüm fonksiyonları hesaplamak floor(n/Q)
için n
. En fazla, Q
fonksiyonları hesaplayacaksınız . Küçük değerleri Q
hesaplamak için daha hızlıdır ancak çok daha fazla alan tüketirken, daha büyük değerleri Q
daha az alan tüketir, ancak hesaplaması daha uzun sürer.
Yöntem 3 ile Q==1
yöntem 1 ile aynı iken yöntem 3 ile yöntem 2 ile Q==inf
aynıdır.
Satranç için bu, her hareketin yanı sıra (her) her 10 tahtadan birini çizerek gerçekleştirilebilir Q==10
.
Yöntem 4:
Eğer tekrarını tersine çevirmek istiyorsanız, yöntemin 3. küçük varyasyon Varsayalım yapabilir Q==100
ve Hesaplamak istediğiniz s[150]
aracılığıyla s[90]
tersten. Değiştirilmemiş yöntem 3 ile, elde etmek için 50 s[150]
, sonra elde etmek için 49 hesaplama daha yapmanız gerekir s[149]
. Ancak s[149]
almak için zaten hesapladığınızdan beri , ilk defa hesaplarken s[150]
bir önbellek oluşturabilirsiniz ve sonra onu görüntülemek istediğinizde zaten önbellekte olacaksınız.s[100]...s[150]
s[150]
s[149]
Yalnızca önbelleğe sen hesaplamak gerekir her zaman yenilenmeye ihtiyacı s[j]
için, j==(k*Q)-1
verilen herhangi k
. Bu sefer, arttırmak Q
daha küçük boyutta (sadece önbellek için), daha uzun zamanlarla (sadece önbelleği yeniden oluşturmak için) sonuçlanacaktır. Q
Durumları ve fonksiyonları hesaplamak için gereken boyutları ve süreleri biliyorsanız, optimal bir değer hesaplanabilir.
Satranç için, bu her hamle ve her 10 panodan birinde (for Q==10
) birer çizim yaparak başarılabilir , ancak hesapladığınız son 10 panodan ayrı bir kağıda çizilmesi gerekir.
Yöntem 5:
Durumlar çok fazla yer kaplarsa veya işlevler çok fazla zaman harcarsa, gerçekte ters oynatmayı uygulayan (sahte değil) bir çözüm oluşturabilirsiniz. Bunu yapmak için, sahip olduğunuz işlevlerin her biri için ters işlevler oluşturmalısınız. Ancak bu, her bir fonksiyonunuzun bir enjeksiyon olmasını gerektirir. Eğer bu yapılabilir ise, o zaman f'
fonksiyonun tersini belirtmek için f
hesaplama s[j-1]
yapmak basittir.
s[j-1] = f'[j-1](s[j], x[j-1])
Burada, işlev ve girişin ikisinin de j-1
olmadığını unutmayın j
. Bu aynı işlev ve girdi, hesaplarsanız kullanmış olacağınız şeyler olurdu.
s[j] = f[j-1](s[j-1], x[j-1])
Bu fonksiyonların tersini oluşturmak zor olan kısımdır. Bununla birlikte, genellikle bir oyunda her işlevden sonra bazı durum verileri genellikle kaybolduğundan, yapamazsınız.
Bu yöntem, olduğu gibi, hesaplamayı tersine çevirebilir s[j-1]
, ancak yalnızca siz varsa s[j]
. Bu, tekrar oynatmaya karar verdiğiniz noktadan başlayarak tekrar oynatmayı izleyebileceğiniz anlamına gelir. İsteğe bağlı bir noktadan geriye doğru oynatmak istiyorsanız, bunu yöntem 4 ile karıştırmanız gerekir.
Satranç için, bu uygulanamaz, çünkü belirli bir tahta ve önceki hamle ile, hangi taşın taşındığını, ancak taşının nerden hareket ettiğini bilemezsiniz.
Yöntem 6:
Son olarak, tüm fonksiyonlarınızın enjeksiyon olduğunu garanti edemezseniz, bunu yapmak için küçük bir numara yapabilirsiniz. Her işlevin yalnızca yeni bir durum döndürmesini sağlamak yerine, attığı verileri de geri göndermesini sağlayabilirsiniz:
s[j+1], r[j] = f[j](s[j], x[j])
r[j]
Atılan veriler nerede . Ve sonra ters fonksiyonlarınızı yaratın, böylece atılan verileri alıp, şöyle:
s[j] = f'[j](s[j+1], x[j], r[j])
Ek olarak f[j]
ve x[j]
, ayrıca saklamanız gerekir r[j]
her fonksiyon için. Bir kez daha, arayabilmek istiyorsanız, yöntem 4'deki gibi yer imlerini saklamanız gerekir.
Satranç için, bu yöntem 2 ile aynı olacaktır, ancak yalnızca hangi parçanın nereye gittiğini söyleyen yöntem 2'den farklı olarak, aynı zamanda her bir parçanın nereden geldiğini saklamanız gerekir.
Uygulama:
Bu her türlü devlet için, her türlü fonksiyon için, belirli bir oyunda çalıştığından, uygulanmasını kolaylaştıracak çeşitli varsayımlar yapabilirsiniz. Aslında, 6 yöntemini tüm oyun durumuyla uygularsanız, sadece verileri tekrar izleyemezsiniz, aynı zamanda zamanda geri dönüp herhangi bir andan itibaren oynamaya devam edebilirsiniz. Bu harika olurdu.
Tüm oyun durumunu kaydetmek yerine, belirli bir durumu çizmeniz için gereken minimum değeri saklayabilir ve bu verileri her sabit zamanda seri hale getirebilirsiniz. Durumlarınız bu serileştirmeler olacak ve girişiniz şimdi iki serileştirme arasındaki fark olacaktır. Bunun çalışması için anahtar, dünya devletinin de az değişmesi durumunda serileştirmenin az değişmesi gerektiğidir. Bu fark tamamen tersinirdir, bu yüzden imleri içeren yöntem 5 kullanmak çok mümkündür.
Bunun bazı büyük oyunlarda uygulandığını, çoğunlukla bir olay (fps'de bir fragman veya spor oyunlarında bir skor) meydana geldiğinde en son verilerin anında oynatılması için uygulandığını gördüm.
Umarım bu açıklama çok sıkıcı değildi.
¹ Bu, bazı programların deterministik olmadığı gibi davrandığı anlamına gelmez (örneğin, MS Windows ^^ gibi). Şimdi ciddi olarak, deterministik bir bilgisayarda deterministik olmayan bir program yapabilirseniz, eşzamanlı olarak Fields madalyası, Turing ödülü ve muhtemelen bir Oscar ve Grammy'yi kazanacağınızdan emin olabilirsiniz.