Wythoff's Nim'i mükemmel oynayın


16

Amacınız Wythoff's Nim oyunu için mükemmel bir oyuncu yazmak .

Wythoff's Nim Kuralları

Wythoff's Nim, aynı sayaçlardan oluşan iki yığınla oynanan deterministik iki oyunculu bir oyundur. Oyuncular bunlardan birini yaptıkları alternatif dönüşler:

  1. İlk desteden bir veya daha fazla sayacı kaldırma
  2. İkinci desteden bir veya daha fazla sayacı çıkarın
  3. Hem birinci desteden hem de ikinci desteden eşit sayıda sayacı (bir veya daha fazla) çıkarın.

Tabii ki, kazıklar negatif olamaz, ancak 0 yapabilir. Hangi oyuncu genel olarak son sayacı kaldırırsa oyunu kazanır.

Daha geometrik düşünen, burada bu uygulamada oynayabileceğiniz eşdeğer bir formülasyon . Tek bir kraliçe, köşesi sol altta olan çeyrek sonsuz bir satranç tahtasının bazı meydanında başlar. Oyuncular, bir satranç kraliçesi gibi hareket eden ancak üç yönle sınırlı olan kraliçeyi hareket ettirir:

  1. Aşağı
  2. Ayrıldı
  3. Çapraz aşağı ve sol

Kraliçeyi köşeye hareket ettiren kazanır.

Kraliçenin koordinatlarını (köşeli (0,0)) ilgili yığınların boyutlarıyla ilişkilendirerek, her iki oyunun da aynı olduğunu görmek kolaydır.

Mükemmel oyun

(Mükemmel oyun ve kazanan hamle kavramlarına aşina iseniz bunu atlayabilirsiniz.)

Wythoff's Nim sonlu ve deterministik bir oyun olduğu için mükemmel bir oyun anlayışına sahiptir . Mükemmel bir oyuncu, her zaman teorik olarak kazanılmış bir konumdan kazanacak bir stratejidir, yani kazanmayı garanti eden bir stratejinin olduğu bir konumdur.

Kazanan bir strateji olmak için, yeni hareket eden ve dolayısıyla bir sonraki oyuncuya gitmeyen oyuncu için her zaman teorik bir kazanma pozisyonuna geçmek için hareket etmek yeterlidir. Bu kazanan konumlardan ilki ( soğuk pozisyonlar da denir ) (0,0), (1,2), (2,1), (3,5), (5,3). Bkz Wikipedia makalesiWythoff's Nim için kazanan bir strateji bulmak için bir algoritmanın açıklaması ve kazanma pozisyonları oluşturmak için bir formül için .

Program gereksinimleri

Bir program veya işlev yazmak , konumu giriş olarak alır ve bu hareketten sonra konum şeklinde kazanan bir hareket çıkarır. En az bayt kazanır.

Kazanan bir hareket yoksa, yani pozisyon teorik bir kayıpsa, programınız bunu belirtmeli ve kaybetmelidir.

Programınızın makul bir süre içinde çalışması gerekir. Bu nedenle, üstel bir özyinelemeli oyun ağacı araması yeterli olmayacaktır. Bir stratejiyi önceden hesaplamak ve kodlamak istiyorsanız, sorun değil.

Giriş

(i,j)Her biri en fazla yığın boyutunu temsil eden negatif olmayan bir çift sayı 99. Bu iki sayı, bir demet, bir liste veya tercih ettiğiniz herhangi bir kap olabilir.

Çıktı

Hareketten sonra pozisyonu tekrar iki sayı veya bir kap olarak yazdırın veya çıktı alın. Bu kazanan bir konuma yasal bir hamle olmalıdır. Bu tür birden fazla hareket varsa, herhangi biri iyi, ancak sadece bir tane.

Kazanan bir hareket yoksa, bunu çıktıda belirtmelisiniz. , 0 veya benzeri herhangi bir çıktı False, yasal bir konum olmadığı sürece ve kaybedilen her giriş için aynıdır.None(-1,-1)

Örnek çalıştırmalar

f(5,0)   = (0,0)
f(2,2)   = (1,2)   # Or (2,1) or (0,0) 
f(1,2)   = False
f(13,9)  = (13,8)  # Or (10,6)
f(10,6)  = False
f(5,10)  = (5,3)
f(25,30) = (8,13)    

2
+1, kısmen sonsuzluğun dörtte biri fikrini sevdiğim için.
Level River St

"makul süre" yi tanımlar. (100,50) için birkaç saniye makul bir süre midir?
John Dvorak

Ah. Bekle. giriş sınırlıdır ... 30 ??? Bu biraz düşük, değil mi?
John Dvorak

@JanDvorak Haklısın, ucuz kısayollara izin verebilir. 99 olarak değiştirildi - Bence bu yeterli mi? Gönderdikten sonra özellikleri düzenlemek için özür dileriz.
xnor

@PeterTaylor Teşekkürler, düzeltildi.
xnor

Yanıtlar:


6

Haskell, 167 165 karakter

c p=o p:c(o p:p)
o p=[(a,b)|a<-[0..],b<-[0..a],(a,b)?p==[]]!!0
(a,b)?p=[y|y@(c,d)<-p,c==a||c==b||d==b||a+d==b+c]
f x@(a,b)|a<b=f(b,a)|x?c[]!!0==x=(0,-1)|1>0=x?c[]!!0

Algoritma verimsiz, ancak <100 girişleri için hala bir saniye içinde (etkileşimli konsolda olmasa da) çalışıyor.

Açıklama:

c p=o p:c(o p:p)

Önceki soğuk konumlar kümesine verilen soğuk konumlar listesi, bir soğuk konumdur, bunu bu konum ve önceki konumlar verilen soğuk konumlar listesidir (Verimsizlik: bu konum iki kez oluşturulur)

o p=[(a,b)|a<-[0..],b<-[0..a],(a,b)?p==[]]!!0

Bir soğuk pozisyon ilk çifttir, öyle ki o çiftten ulaşılabilecek hiçbir soğuk pozisyon yoktur (Verimsizlik: bunun yerine önceki çiftten arama yapmalıyız)

(a,b)?p=[y|y@(c,d)<-p,c==a||c==b||d==b||a+d==b+c]

Bir çiftten erişilebilen konumlar, ilk öğelerinin eşleştiği, ikinci öğelerinin eşleştiği, fark eşleşmelerinin veya kaldırılmadan önceki daha küçük yığının, kaldırıldıktan sonraki daha büyük yığın olacağı şekildedir.

f x@(a,b)|a<b=f(b,a)
         |x?c[]!!0==x=(0,-1)
         |1>0=x?c[]!!0

(ana yöntem) Yığınlar yanlış sıradaysa değiştiriniz. Aksi takdirde, bir konumdan erişilebilen ilk soğuk konum konumun kendisiyse, arızayı belirtin (ideal olarak Maybe (Int,Int)bunun yerine geri dönülür ). Başka soğuk pozisyona geri dön (Verimsizlik: bahsedilen çift iki kez yukarıya bakılır. Daha da kötüsü, soğuk pozisyon listesi iki kez oluşturulur)


" Öyleyse, üstel bir özyinelemeli oyun ağacı araması yeterli olmayacak " iyimser görünüyor, çünkü tarif ettiğiniz şey tam olarak böyle geliyor.
Peter Taylor

@PeterTaylor bu O (n ^ 4). Her soğuk çiftin bulunması O (n ^ 3) zaman alır ve bunlardan O (n) vardır. Üretimi optimize etmek, O (n ^ 2) 'ye getirecektir (diziyi doğru okursam). Bir üstel zaman algoritması çok daha yavaş olacaktır. Bazı testler yapmalı mıyım?
John Dvorak

Tamam, sana inanıyorum.
Peter Taylor

x@-den kaldırabilirsinx@(a,b)?p=...
gururlu haskeller

Oraya nasıl geldiğinden emin değilim. Düzeltme, teşekkürler.
John Dvorak

5

GolfScript ( 63 57 bayt)

{\]zip{~-}%0|$(!*,1=}+1,4*{..,,^[(.@,2/+.2$]+}38*2/?0]0=`

Formdaki stdin'den girdi bekler [a b]ve çıktıyı stdout'a bu formda veya 0kaybedilen bir pozisyonda bırakır . Çevrimiçi demo

genel bakış

,4*{..,,^[(.@,2/+.2$]+}38*2/

Beatty dizi özelliğini kullanarak [b a]soğuk pozisyonların bir listesini ( her soğuk pozisyon için çevrilmiş versiyon dahil) hesaplar .[a b]

Sonra ?tarafından oluşturulan bloğu tatmin eden ilk soğuk pozisyonu arar

{\]zip{~-}%0|$(!*,1=}+

Bu konum, ya olduğunu doğrulayan sonra vektör farkının hesaplanması ve yan giriş pozisyonundan ulaşılabilir olduğunu temelde kontrol [0 x], [x 0]veya [x x]bazıları için x > 0. IMO bu test zeki biraz 0|$forma bu biçimlerden birinde bir dizi kuvvetleri [0 x]eşleme süre [0 0]için [0], [a b]ne ade bbir 0üç elemanlı bir dizi için, ve [-x 0]ya [-x -x]hiç [-x 0]. Sonra (!*,1=kontrol eder [0 x].

Son olarak 0]0=`, geri dönüş durumu ve çıktı için biçimlendirme yapar.


4

Pyth 57 58 61 62

K1.618Jm,s*dKs*d*KKU39M<smf|}TJ}_TJm,-Ghb-Hebt^,0hk2U99 1

Çevrimiçi deneyin.

Diğer cevaplara oldukça benzer, ancak bu wikipedia sayfası devam etmek için çok şey vermedi;) Sihirli sayı 39, <değerleri olan soğuk konumların sayısıdır 99.

Bir işlevi tanımlar gsizin gibi arayabileceğiniz g 30 25. İade []başarısızlık, [(13,8)]başarı üzerine.

açıklama

K1.618                            : K=phi (close enough for first 39 values)
      Jm,s*dKs*d*KKU39            : J=cold positions with the form (small,big)
M<s                              1: Define g(G,H) to return this slice: [:1] of the list below 
   mf|}TJ}_TJ                     : map(filter: T or reversed(T) in J, where T is each member of..
             m,-Ghb-Hebt^,0hk2    : [(G H) - (0 x+1),(x+1 0) and (x+1 x+1)]
                              U99 : for each x from 0 - 98

s, içine birkaç karakter kazandırır /____1. tekli aralık kullanılarak rZ39değiştirilebilir U39. Benzer şekilde, r99)ile değiştirebilirsiniz U99.
isaacg

@isaacg Teşekkürler! Tamamen unuttum U. Açıklamayı gerçekten güncellemeliyim: P
FryAmTheEggman

@isaacg Sadece Pyth hakkında bir düşünce, sanırım @ikinci argümanı şimdi bir liste ise küme kesişimini gerçekleştirebilirsiniz. aDeğiştirildiğinden beri biraz garip bir şekilde dışarıda kaldı : P
FryAmTheEggman

Bu iyi bir fikir - uyguladım. Ayrıca kavşak kodunu, iki liste listesinin kesişimini almak da dahil olmak üzere, daha önce mümkün olmayan birkaç numaraya izin verecek şekilde değiştirdim.
isaacg

2

Javascript ES6 - 280 bayt

küçültülmüş

r=x=>~~(x*1.618);g=(y,x)=>y(x)?g(y,x+1):x;s=A=>A?[A[1],A[0]]:A;f=(a,b)=>j([a,b])||j([a,b],1);j=(A,F)=>l(A,F)||s(l(s(A),F));l=(A,F)=>([a,b]=A,c=(F&&a+b>=r(b)&&(e=g(x=>a+b-2*x-r(b-x),0))?[a-e,b-e]:(e=g(x=>r(a+x)-2*a-x,0))+a<b?[a,a+e]:(e=r(b)-b)<a?[e,b]:0),c&&r(c[1]-c[0])==c[0]?c:0)

Expanded

r = x => ~~(x*1.618);
g = (y,x) => y(x) ? g(y,x+1) : x;
s = A =>A ? [A[1],A[0]] : A;
f = (a,b) => j([a,b]) || j([a,b],1);
j = (A,F) => l(A,F) || s(l(s(A),F));
l = (A,F) => (
    [a,b] = A,
    c = (
        F && a+b >= r(b) && (e = g( x => a+b - 2*x - r(b - x), 0 )) ? [a-e,b-e] :
        (e = g( x => r(a+x) - 2*a - x, 0)) + a < b ? [a,a+e] :
        (e = r(b) - b) < a ? [e,b] : 0
    ),
    c && r(c[1] - c[0]) == c[0] ? c : 0
);

Güzel ve hızlı algoritma. O (n) cinsinden çalışır, ancak bayt tasarruf döngüsü için değilse sabit sürede çalışır. Döngü özyinelemeli bir artırıcı olarak uygulanır, bu nedenle komut dosyası nihayet yüzlerce veya binlerce n için yineleme hatasıyla başarısız olur . Bay Taylor'ın bahsettiği Beatty dizi özelliğini kullanır, ancak diziyi hesaplamak yerine, doğru terim (ler) e yükselir.

Test ettiğim tüm test girişlerinde ve birçok düzinelerce düzgün çalışıyor.

Çağrılacak işlev f. Başarı ve 0vazgeçme üzerine bir dizi döndürür .


Bekle, bir dizi çıktısı iyi mi?
John Dvorak

@JanDvorak: xnor geçerli çıktılar listesinde listelenen bir tuple var, bu yüzden düşündüm. Belki konuyu açıklığa kavuşturabilir. Her durumda önemsiz bir düzeltme.
COTO

Bir dizi veya çiftin tekli dizisi iyidir; birden fazla kazanan hamle değildir.
xnor

1

Perl 5-109 (2 bayraklı)

#!perl -pl
for$a(@v=0..99){for$b(@v){$c=$a;$d=$b;${$:="$a $b"}||
map{$$_||=$:for++$c.$".++$d,"$a $d","$c $b"}@v}}$_=$$_

Kullanımı:

$ perl wyt.pl <<<'3 5'

$ perl wyt.pl <<<'4 5'
1 2

Her olası giriş için çözümü hesaplar, ardından tek bir arama yapar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.