Tic-tac-toe oyna ve asla kaybetme


14

(En iyi stratejiyi kullanmanız gereken bazı zorluklar vardır, ama işte burada değiliz. Kazanabilseniz bile beraberlik yapmanıza izin verilir)

Meydan okuma

Oyunu tic-tac-toe oynayan bir program yazın. Kaybetmemelidir (bu nedenle oyunu beraberlikle ya da kazanarak bitirmelidir).

İzin verilen G / Ç yöntemleri

  1. Giriş geçerli kart olabilir. 2. oyuncunun önceki tüm hamlelerinin motorunuz tarafından oynandığını varsayabilirsiniz.
  2. Giriş ilk oyuncunun hamleleri olabilir ve fonksiyonunuz geçmişte gerçekleşen hamleleri depolar. Bu durumda işlev, her hareket için bir defa olmak üzere birden çok kez çağrılır; veya işlev / program bilgi istemi girdisini birden çok kez kullanın.
  3. İlk oyuncu sizseniz ekstra bir giriş yapmanıza izin verilir veya birinci oyuncu problemini ve ikinci oyuncu problemini çözmek için iki (muhtemelen ilgili) işlev yazabilirsiniz. Programınızın giriş yöntemi 2'yi (çoklu çağrı) kullanması gerekiyorsa, ilk çağrıda neyin geçirileceğine karar verebilirsiniz.
  4. Çıkış sizden sonra kart olabilir.
  5. Çıktı sizin hamleniz olabilir.
  6. Bir hareket, bir çift sayı (0-indeksleme veya 1-indeksleme olabilir), 0 ~ 8 aralığındaki bir sayı veya 1 ~ 9 aralığındaki bir sayı olarak temsil edilebilir.
  7. Kart 3 × 3 dizisi veya uzunluk 9 dizisi olarak gösterilebilir. Dilin 0 dizinleme dizisi olsa bile, 1 dizinleme kullanabilirsiniz.
  8. Izgara üzerinde hücreler belirtmek için herhangi bir 3 farklı değerler kullanabilir X, Ove boşaltın.

Kazanma kriterleri

Her dilde en kısa kod kazanır.


Size bir kayıp verilirse çözümünüz geçersizdir. Diğerleriyle oynuyorsunuz, bu yüzden satranç tahtası anında değişmeyecek, bu yüzdenwe can assume that all previous moves of the 2nd player were also played by our engine
l4m2


1
@ l4m2 Yorumlayıcıyı yeniden başlatmanız yeterli. Bitti. Neden rahatsız oluyorsun? Sadece gereksiz yere hiçbir şey için bayt sayısını arttırır.
user202729

1
Bir hatırlatma olarak, yorumdaki meydan okumayı değiştirmemelisiniz .
user202729

4
Bonusu yapma. Gerek ya da kaldırın, isteğe bağlı yapmayın. Bonus mücadeleyi
mahveder

Yanıtlar:


4

Befunge, 181 168 bayt

>>4&5pp20555>>03>16p\::5g8%6p5v
 ^p5.:g605$_ #!<^_|#:-1g61+%8g<
543217539511|:_^#->#g0<>8+00p3+5%09638527419876
v<304p$_v#:->#$$:2`#3_:^
>#\3#13#<111v124236478689189378

Karttaki konumlar 1 ila 9 arasında numaralandırılır. Varsayılan olarak ilk hamleyi alırsınız, ancak bilgisayarın önce gitmesine izin vermek istiyorsanız, ilk hamleniz için 0 girebilirsiniz. Bir hamle yaptığınızda, bilgisayar hamlelerini gösteren bir sayı ile cevap verecektir.

Geçerli bir hamle girmediğinizden emin olmak için bir kontrol yoktur ve ayrıca herhangi birinin kazanıp kazanmadığını görmek için hiçbir kontrol yoktur. Bir kez daha yapılacak hamle olmadığında, program sonsuz bir döngüye girer.

Etkileşimli girdili çevrimiçi tercümanlar olmadığından bunu çevrimiçi olarak test etmek biraz zor. Bununla birlikte, hangi hareketleri önceden yapacağınızı biliyorsanız (bu, bilgisayarın nasıl tepki vereceğini bildiğinizi varsayar), bu hareketleri önceden programlanmış olarak TIO üzerinde test edebilirsiniz.

Kullanıcı önce oynatır: Çevrimiçi deneyin!
Bilgisayar önce oynuyor: Çevrimiçi deneyin!

Neler olup bittiğini görmeyi kolaylaştırmak için, hamle arasında tahta çıkaran bir versiyonum var.

Kullanıcı önce oynatır: Çevrimiçi deneyin!
Bilgisayar önce oynuyor: Çevrimiçi deneyin!

Sonuçları görmeden önce TIO'nun zaman aşımına uğramasını beklemeniz gerektiğini unutmayın.

açıklama

Kart, Befunge bellek alanında 1'den 9'a kadar indekslenmiş 9 değerlik düz bir dizi olarak saklanır. Bu, bilgisayarın ilk oynatılmasına izin vermek istediğimizde sıfır ofsetini özel bir durum "hareketsiz" olarak kullanmamızı sağlar. Oyuncu hareketleri 4 olarak ve bilgisayar 5 olarak hareket eder. Başlamak için tüm konumlar 32'ye (Befunge bellek varsayılanı) başlatılır, bu nedenle panoya her eriştiğimizde 8 ile modlanırız, böylece 0, 4'e geri döneriz veya 5.

Bu düzenleme göz önüne alındığında, tahtadaki herhangi bir üç pozisyonun değerlerini toplarsak, toplam 10 ise bilgisayarın kazanmaktan bir hamle uzakta olduğunu, toplam 8 ise oyuncunun kazanmaktan bir hamle uzakta olduğunu biliyoruz. toplam 9 ise konumlar bilgisayar ve oynatıcı arasında paylaşılır (ancak yine de bir konum ücretsizdir).

Tüm stratejimiz bu konsepte dayanmaktadır. Tahtadaki üç konum kümesini gösteren üçlü bir listeyi alan bir rutinimiz var, bu konumların toplamını hesaplıyoruz ve toplam belirli bir toplama eşitse, bilgisayar setteki konumlardan hangisinin serbest olduğuna hareket eder.

Test ettiğimiz üçlülerin ana listesi kazanan kombinasyonlardır (1/2/3, 1/5/9, 1/4/7, vb.). Önce toplam 10 (bilgisayar kazanmak üzere) ve sonra toplam 8 (oyuncu kazanmak üzere ve bu hareketi engellememiz gerekiyor) arıyoruz. Daha azı belli ki, toplamda 9'u da kontrol ediyoruz (eğer oyuncu ve bilgisayarın her biri pozisyonlardan birine sahipse, bu üçüncüyü almak için iyi bir stratejidir).

Bu son senaryo öncesinde, yaptığımız diğer stratejik hareket, tüm köşe setlerini (1/2/4, 2/3/6 vb.) Ve iki karşıt köşe kombinasyonunu (1/8/9 ve 3) kontrol etmektir. / 7/8). Bu kombinasyonlardan herhangi biri 8 ise, yani oyuncu iki pozisyon aldıysa, bilgisayarın kalan boş pozisyonu alması iyi bir stratejidir.

Son olarak, iki özel durum hareketi vardır. İlk olarak, her zaman başka bir hareketten önce merkez konumunu almaya çalışırız. Bu, diğer tüm hareketlerimizle aynı rutinle elde edilir, sadece tek bir üçlü, 5/5/5 ve hedef toplamı 0'dan geçer. Ek olarak, tüm diğer testler bir hareket bulamadıysa, son çare olarak üst köşelerden biri. Yine bu basitçe 1/1/1 ve 3/3/3'lük üçlülerin hedef toplamı 0 ile test edilmesiyle elde edilir.

Bunun mutlaka mükemmel bir strateji olduğunu düşünmüyorum - potansiyel olarak kazanılmış olabilecek bilgisayarın çektiği oyunlar olabilir - ama asla bir maçı kaybetmeyecek kadar iyi. Bilgisayara karşı olası her hamleyi oynamaya çalışan bir test komut dosyası çalıştırdım ve her geçerli hamle sırası için bilgisayar oyunu kazandı veya çekti.


Befunge'yi tam olarak bilmiyorum, ama belki tüm olası girişleri test edebilirsiniz ( Örnek )
l4m2

@ l4m2 FYI, şimdi bilgisayara karşı her olası hareketi deneyen ve asla kaybetmediğini onaylayabilen bir test komut dosyası çalıştırdım.
James Holderness

2

Python 2: 399 401 349 333 317 370 bayt

2x Hata Düzeltmesi: l4m2'ye kredi

-52 karakter: yeraltı monorayına kredi

-16 karakter: Jonathan Frech'e kredi

-26 karakter: kullanıcıya kredi202729

def f(b):
 t=4,9,2,3,5,7,8,1,6;n=lambda k:[t[i]for i,j in enumerate(b)if j==k];p,o,a,I=n(2),n(1),n(0),t.index
 for i in p:
    for j in p:
     for k in a:
        if i+j+k==15and-j+i:return I(k)
 for i in o:
    for j in o:
     for k in a:
        if i+j+k==15and-j+i:return I(k)
 for i in 9,3,7,1:
    if i in a and 5 in p:return I(i)
 for i in 5,4,2,8,6:
    if i in a:return I(i)
 return I(a[0])

Çevrimiçi deneyin!

Geçen yarıyıl aldığım doğrusal cebir kursunun ilk gününde, zeki yüksek lisans öğrencisi eğitmenim, tic-tac-toe board'u matris olarak temsil ederseniz:

4 | 9 | 2
--+---+--
3 | 5 | 7
--+---+--
8 | 1 | 6

daha sonra arka arkaya üç elde etmek, aralıkta [1,9] 15'e kadar olan üç sayı seçmeye eşdeğerdir. Bu yanıt bu fikirden yararlanır. Fonksiyon, tahtayı temsil eden dokuz sayı içeren bir liste alır. 0 boş bir alanı gösterir, 1 rakip tarafından işgal edilir ve 2 program tarafından yapılan önceki bir oyunu temsil eder. İlk 3 satır, programın hangi sayıları seçtiğini (p), muhalefet (o) 'yı seçtiğini ve hala kullanılabilir durumda olduğunu (a) gösterir. Daha sonra mevcut sayıları inceler ve daha önce seçmiş olduğu iki sayı ile birleştiğinde on beşe ekler. Eğer yaparsa, o kareyi seçecek ve kazanacaktır. Eğer hemen kazanma hamlesi yoksa, rakibin aynı yöntemi kullanarak kazanıp kazanamayacağını kontrol eder. Eğer yapabilirlerse, kazanan karelerini alacaktır. Ne kazanan ne de engelleme hareketi mevcutsa, bir köşede hareket edecek. Bu bir aptal arkadaşı önler:

- - - 
- X -
- - -

- O -             # Bad Move
- X -
- - -

- O X
- X -
- - -

- O X
- X -
O - -

- O X
- X -
O - X

Bu durumlardan hiçbiri meydana gelmezse, keyfi olarak bir kare seçecektir. Fonksiyon, algoritma tarafından seçilen 0 indekslenmiş kareyi temsil eden bir sayı [0,8] çıkarır.

Düzenleme: Algoritma şimdi merkeze diyagonal üzerinde öncelik verir, bu da l4m2 ve ilgili stratejilerle gösterilen başka bir aptal eş olasılığını önler.

Düzenleme: Açıklığa kavuşturmak için, işlev bir dizi şeklinde bir tahtayı alır ve [0,8] üzerinde tamsayı olarak bir hareket çıkarır. Bu G / Ç stratejisi çok karmaşık olduğu için, onu daha etkileşimli hale getiren bir sarıcı komut dosyası. Tek bir komut satırı argümanı alır; bu, oyuncu önce giderse 1, program önce giderse 0 olmalıdır.

import sys

def f(b):
 t=4,9,2,3,5,7,8,1,6;n=lambda k:[t[i]for i,j in enumerate(b)if j==k];p,o,a,I=n(2),n(1),n(0),t.index
 for i in p:
    for j in p:
     for k in a:
        if i+j+k==15and-j+i:return I(k)
 for i in o:
    for j in o:
     for k in a:
        if i+j+k==15and-j+i:return I(k)
 for i in 9,3,7,1:
    if i in a and 5 in p:return I(i)
     for i in 5,4,2,8,6:
        if i in a:return I(i)
 return I(a[0])

board = [0,0,0,0,0,0,0,0,0]
rep = {0:"-",1:"X",2:"O"}

turn = int(sys.argv[1])
while True:
    for i in range(3):
        print rep[board[i*3]]+" "+rep[board[i*3+1]]+" "+rep[board[i*3+2]]
        print
    if turn:
        move = int(raw_input("Enter Move [0-8]: "))
    else:
        move = f(board)
    board[move] = turn+1
    turn = (turn+1)%2 


1
returnSonuncusu hariç tüm hatlarınız onlardan önce satır üzerine
undergroundmonorail

1
Ayrıca ben yardım ancak yerine yapmanın, bayt kaydetmek olsaydı merak olamaz e=enumerate, do f=lambda n:[t[i]for i,j in enumerate(b)if j==n]ve atama p, ove aişlevini kullanarak. Yine
undergroundmonorail

3
Hala hacklendi . xkcd.com/832 gerçekten yardımcı oluyor
l4m2

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.