MENACE'yi uygula


11

Arka fon

MENACE ( M achine e ducable N oughts bir nd Rosses e ngine) 1960 İngiliz bilgisayar bilim adamı Donald Michie tarafından oluşturulan oyun Noughts ve çarpılar için bir temel sığ makine öğrenme algoritmasıdır. Başlangıçta, her biri bir tahta pozisyonu ile etiketlenmiş ve renkli boncuklar (olası hareketleri temsil eden dokuz renkten biriyle) içeren 304 kibrit kutusu ile uygulandı. Michie, bu 304 kibrit kutusunun tahtadaki her hareket kombinasyonu için yeterli olduğunu hesapladı.

Aranızda ne kadar matematiksel bir N&C tahtasında aslında 19.683 olası Noughts, Crosses ve Blanks kombinasyonu olduğunu fark edebilirsiniz; ancak, bu sayıyı azaltmanın yollarını hesapladı (algoritmayı hızlandırmak ve muhtemelen kibrit kutularını azaltmak için!). İlk olarak, mümkün olmayan tüm hareketleri kaldırdı, örneğin:

-------
|X|0|X|
| |0| |
|X|X| |
-------

(iki nought ve dört çarpı)

Sonra, rotasyonları telafi etti. Örneğin, kibrit kutusunda:

-------
| |0|0|
|X| |X|
| |0| |
-------

aynı kutuyu kullanabiliriz

-------
| |X| |
|0| |0|
| |X|0|
-------

Bu nedenle, yukarıda belirtilen renkli boncuklar mutlak olanları değil, göreceli konumları temsil eder. Örneğin, kırmızı bir boncukun sol üst demek olduğunu söyleseydik, kutunun üstündeki resme bakar ve şunu görürdük:

-------
| |0|0|
|X| |X|
| |0| |
-------

bu yüzden bunun tahta olması durumunda kırmızı boncukların şu anlama geleceğini biliyoruz:

-------
|R|0|0|
|X| |X|
| |0| |
-------

Ama bu yönetim kurulu ise:

-------
| |X| |
|0| |0|
| |X|0|
-------

kırmızı boncuk

-------
| |X|R|
|0| |0|
| |X|0|
-------

Bu dönüşümler rotasyon ve ters çevirme için uygulanmıştır (diyagonal dahil tüm yönlerde). Bir kez daha, her kibrit kutusunu bu şekilde yalnızca bir kez kaydetmeniz gerekir: her dönüşüm için ayrı sanal kutular yapmayın!

Michie'nin yaptığı bir başka sadeleştirme, bilgisayarın önce gittiğinden emin olmaktı. Bu şekilde, kalan tüm kutuların yaklaşık beşte birini kaldırarak tüm birinci seviye hareketleri kaldırabilirdi. Son olarak, tüm oyun sonu kutularını kaldırdı (bu adımlarda daha fazla 'içerik' veya hamle gerekmediği için).

Doğru, şimdi algoritmanın kendisine (çok basit):

  1. İlk olarak, boncukların renklerinin neyi temsil ettiğine karar verin. Tahtadaki boşlukların her birini temsil etmek için 9 renge ihtiyacınız olacak.
  2. Oyunun başlangıcında, 304 kibrit kutusunun her biri boncuklar içerir. Boncuklar rastgele renkteyken (kopyalar iyi), olası hareketler olmalıdır (eğer tahta durumu görüntüsü sağ ortada bir 'O' gösteriyorsa, ortası temsil eden boncuk kullanamazsınız. sağ).
  3. MENACE'in (X) dönüşü her defasında, geçerli tahta konumu (veya üzerinde bir miktar dönüşüm) basılı olan kibrit kutusunu bulun.
  4. Kibrit kutusunu açın ve oradaki herhangi bir boncuğu rastgele seçin.
  5. Kibrit kutusundaki görüntüye ulaşmak için kart durumunun nasıl dönüştürüldüğünü bulun (örneğin saat yönünün tersine 90 derece döndürülmüş). Ardından, bu dönüşümü boncuğa uygulayın (örn. Sol üst sol-sol olur).
  6. O kareye bir X yerleştirin. Seçilen boncuğu kibrit kutusundan çıkarın. Sonuç olarak kutu boş bırakılırsa, kutuya üç rastgele (olası) boncuk koyun ve hareket için birini seçin.
  7. Oyun bitene kadar 3-6 tekrarlayın.
  8. MENACE oyunu kazanırsa, MENACE'in aldığı her kibrit kutusunu gözden geçirin. Ardından, o harekette hangi renk boncuğunu kullandığını takip edin. Bu boncuk renginden ikisini kutuya koyun (böylece orijinal boncuk + bir tane daha olacak, böylece MENACE'nin bu konuma bir dahaki sefer ilerlemesini sağlama olasılığı artacak)
  9. MENACE oyunu kaybederse, hiçbir şey yapmayın ( çıkardığı boncukları değiştirmeyin).
  10. MENACE oyunu çizdiyse, her hareketinde kullandığı boncuğu değiştirin, ancak fazladan bir tane eklemeyin, böylece başladığınız şeyden ayrılırsınız.

Bu bize çok basit ama uygulanması zor bir algoritma bırakıyor. Bu, mücadelenizin temelini oluşturur.

Hala kafanız karıştıysa, bkz. Http://chalkdustmagazine.com/features/menace-machine-educable-noughts-crosses-engine/ - bu algoritmayı ilk öğrendiğimde okudum

Meydan okuma

Bilgisayar ile Tic-Tac-Toe oyunu oyna. Her adımda, tüm kibrit kutularının içeriğini çıktılayın.

Girdiler

  • Programın başında MENACE'ye karşı kaç oyun oynamak istediğinizi söyleyen bir sayı
  • Ardından, MENACE'in ilk turundan sonra, hareketinizi iki karakterli bir dize olarak girersiniz, ilk harf Y eksenine karşılık gelen "L", "R" veya "M" (sol, sağ veya orta) olur. Sonra bu kez X eksenine atıfta bulunarak başka bir harf (tekrar, "L", "R" veya "M") girersiniz. Tüm hamleler ve oyunlar için tekrarlayın.

çıktılar

  • Her yeni oyunun başında "yeni oyun" çıktı.
  • Oyuncunun her hamlesinden sonra, kartı herhangi bir makul formatta çıkartın. Güzel görünmesi gerekmez (örneğin, tahtanın konumlarını temsil eden bir dizi dizi iyidir).
  • Oyuncunun her hamlesinden sonra, MENACE bir hamle yapmalıdır. MENACE'in hareketinden sonra kartın çıktısını al
  • Her oyundan sonra, tüm 304 kibrit kutusunun içeriğini çıkartın. Boncuklar bir harf, bir renk adı, karakter veya istediğiniz herhangi bir dize veya tamsayı ile gösterilebilir (işaretçi, anonim işlev vb. Yok).

kurallar

  1. Bu , bayt en kısa cevap kazanır.
  2. MENACE'nin yanıtını gördükten sonra hamle girebilmeliyim. Hayır 'tüm hamlelerini bu işleve geçir ve oyunun nasıl oynandığını izle'.
  3. Oyunlar arasında tahta temizlenmelidir.
  4. Kibrit gerekir değil (bu makine öğrenimini sıfırlamak olurdu) oyunlar arasında silinebilir
  5. 304 kibrit kutunuz olmalıdır. Herkes bu algoritmayı tüm 19.683 kibrit kutusuyla uygulayabilir, ancak öğrenme yavaştır (çünkü hepsini yararlı içeriklerle elde etmek için birçok oyun gerektirir ).
  6. Çıktı herhangi bir makul formatta olabilir ve girdi PPCG standartlarına göre alınabilir (kural 2'ye uygun olduğu sürece). Giriş biçimini ayarlamanız gerekirse (' Giriş ' bölümünde açıklandığı gibi), mantıklı olduğu sürece sorun olmaz.
  7. Oyun bir oyuncu kazandığında (çapraz, yatay veya dikey olarak üç sırada) veya beraberlik varsa (tahta dolu ve kazanan yok)
  8. MENACE'in olası hamleleri yapması (ve sadece her kibrit kutusunun içinde olası boncukların olması) gerekmesine rağmen, meydan okuma için kullanıcının girişini doğrulamanız gerekmez. Yanlış bir şey yazarlarsa, programınız her şeyi yapabilir (tamamen delir, hata atma vb.) - girişin doğru olduğunu varsayabilirsiniz.

Martin Gardner'ın yaptığı daha basit oyunu Hexapawn kullanarak gösterdiğini hatırlıyorum, ancak yaptığı “bilgisayar” adını verdiği şeyi unutuyorum.
Neil



1
Büyük zorluk. Birkaç hızlı soru: 1. Bir kutuda belirli bir alanda birden fazla boncuk olduğunda, bu çıktıda nasıl temsil edilmelidir? 2. Her hamleden sonra 304 kutunun (2736 hücre) çıkışını gerçekten istiyor musunuz?
Nick Kennedy

@NickKennedy Geri bildiriminiz için teşekkür ederiz. Eğer boncuklar temsil etmek numaralarını seçerseniz yolu ben, bu günlüğe ne zaman boncuk (eğer farklı yapmak farklı dilleri kısıtlamak değil gerçi) bir dizi olarak temsil edileceği üzere mesela beklediğiniz: [[0, 2, 6], [4, 8, 4, 3, 3], [7, 7, 7, 7, 7, 7, 7, 8], [1], ... [3, 3, 5, 4]].
Geza Kerecsenyi

Yanıtlar:


3

R , 839 bayt

options(max.print=1e5)
s=colSums
r=rowSums
m=matrix
a=array
y=apply
S=sum
p=sample
b=m(rep(i<-1:(K=3^9),e=9)%/%(E=3^(8:0))%%3,c(9,K))
V=a(1:9,c(3,3,8))
V[,,2:4]=c(V[x<-3:1,,1],V[,x,1],V[x,x,1])
V[,,5:8]=y(V[,,1:4],3,t)
d=aperm(a(b[c(V),],c(9,8,K)),c(1,3,2))
v=m(V,9)
g=y(m(match(e<-y(d*E,2:3,S),i),,8),1,min)
g[K]=K
G=9-y(t(e==g)*8:1,2,max)
h=s(a(c(b,d[,,5],b[c(1,5,9,3,5,7,1:3),]),c(3,3,K,3))*3^(0:2))
k=r(s(h==13))>0
l=r(s(h==26))>0
o=s(b>0)
M=b
M[M==0]=-1
repeat{A=b[,t<-K]
z=j=c();B=1
repeat{if(S(pmax(-M[,t],0))<1)M[p(9,pmin(3,S(x)),,x<-M[,t]<1),t]=-1
z=c(z,u<-p(9,1,,pmax(-M[,t],0)))
j=c(j,t)
A[v[,G[B]][u]]=1
print(m(A,3))
if(k[B<-S(A*E)]||o[B]==9)break
A[ceiling((utf8ToInt(readline())-76)/5)%*%c(1,3)+1]=2
if(l[B<-S(A*E)])break
t=g[B]}
M[x]=M[x<-cbind(z,j)]-k[B]+l[B]
print(a(M[,g==seq(g)&!k&!l&s(b==1)==s(b==2)&o<8],c(3,3,304)))}

Çevrimiçi deneyin!

Oldukça uzun bir cevap, ama bu basit bir meydan okuma değildi. Buradaki TIO bağlantısı etkileşimli girdi beklediğinden başarısız olacaktır. İşte rastgele bir nokta seçen ikinci, rastgele bir oyuncuya karşı oynayan bir versiyon . Bu ikinci versiyonun çıktısı sadece kazanır (beraberlik için 1, 2 veya 0). Kibrit kutuları tüm tahta pozisyonları için başlatılır, ancak sadece spesifikasyon başına 304 için kullanılır. Her pozisyondaki boncuk sayısını belirtmek için tahtanın negatif sayıları olan kopyaları olarak uygulanırlar. Orijinal özellik başına vektörlerin bir listesini denedim, ancak daha az sezgiseldi.

Bu yorumlar ile daha az golfçü bir versiyonu (ama yine de kısa değişken isimleri). Çok uzun olduğu için kibrit kutularını basmadığını unutmayın. Oyuncu 2 için interaktif bir oyuncu 2, rastgele bir oyuncu 2 veya aynı kibrit kutusu stratejisini uygulayabilir.

auto = 1 # 1 = Random player 2, 2 = Player 2 uses learning strategy
         # 0 for interactive
print_board <- function(board) {
  cat(apply(matrix(c(".", "X", "O")[board + 1], 3), 1, paste, collapse = ""), "", sep = "\n")
}
E = 3 ^ (8:0) # Number of possible arrangements of board
              # ignoring rotations etc.
# Make all possible boards
b = matrix(rep(1:3 ^ 9, e = 9) %/% E %% 3, c(9, 3 ^ 9))
# Define the eight possible rotation/inversion matrices
V = array(1:9, c(3, 3, 8))
V[, , 2:4] = c(V[x <- 3:1, , 1], V[, x, 1], V[x, x, 1])
V[, , 5:8] = apply(V[, , 1:4], 3, t)
# Create eight copies of the 19683 boards with each transformation
d = aperm(array(b[c(V), ], c(9, 8, 3 ^ 9)), c(1, 3, 2))
v = matrix(V, 9)
# Create reverse transformations (which are the same except for rotation)
w = v[, c(1:5, 7, 6, 8)]
# Find the sums of each transformation using base 3
e = apply(d * E, 2:3, sum)
# Find the lowest possible sum for each board's transformed versions
# This will be the one used for the matchboxes
f = matrix(match(e, 1:3 ^ 9), , 8)
g = apply(f, 1, min)
# Store which transformation was necessary to convert the lowest board
# into this one
G = 9 - apply(t(e == g) * 8:1, 2, max)
# Work out which boards have 3-in-a-row
h = colSums(array(c(b, d[, , 5], b[c(1, 5, 9, 3, 5, 7, 1:3), ]), c(3, 3, 3 ^ 9, 3)) * 3 ^ (0:2))
k = rowSums(colSums(h == 13)) > 0 # player 1 wins
l = rowSums(colSums(h == 26)) > 0 # player 2 wins
# Store how many cells are filled
o = colSums(b > 0)
# Create matchboxes. These contain the actual board configuration, but
# instead of zeroes for blanks have a minus number. This is initially -1,
# but will ultimately represent the number of beads for that spot on the
# board.
M = b
M[M == 0] = -1
repeat {
  # Initialise board and storage of moves and intermediate board positions
  A = b[, t <- 3 ^ 9]
  z = j = c()
  C = 1
  # If we're automating player 2 also, initialise its storage
  if (auto) {
    Z = J = c()
  }
  repeat {
    # If the current board's matchbox is empty, put up to three more beads
    # back in
    if (sum(pmax(-M[, t], 0)) == 0) {
      M[sample(9, pmin(3, sum(x)), , x <- M[, t] == 0), t] = -1
    }
    # Take out a bead from the matchbox
    u = sample(9, 1, , pmax(-M[, t], 0))
    # Mark the bead as taken out
    M[u, t] = M[u, t] + 1
    # Store the bead and board position in the chain for this game
    z = c(z, u)
    j = c(j, t)
    # Mark the spot on the board
    A[v[, C][u]] = 1
    # Print the board
    if (!auto) print_board(matrix(A, 3))
    # Check if  player 1 has won or board is full
    if (k[B <- sum(A * E)] || o[B] == 9) break
    if (auto) {
      # Repeat for player 2 if we're automating its moves
      # Note if auto == 1 then we pick at random
      # If auto == 2 we use the same algorithm as player 1
      D = g[B]
      if (sum(pmax(-M[, D], 0)) == 0) {
        M[sample(9, pmin(3, sum(x)), , x <- M[, D] == 0), D] = -1
      }
      U = sample(9, 1, , if (auto == 1) M[, D] <= 0 else pmax(-M[, D], 0))
      Z = c(Z, U)
      J = c(J, D)
      A[v[, G[B]][U]] = 2
    } else {
      cat(
        "Please enter move (LMR for top/middle/bottom row and\nLMR for left/middle/right column, e.g. MR:"
      )
      repeat {
        # Convert LMR into numbers
        q = ceiling((utf8ToInt(readline()) - 76) / 5)
        if (length(q) != 2)
          stop("Finished")
        if (all(q %in% 0:2) && A[q %*% c(1, 3) + 1] == 0) {
          break
        } else {
          message("Invalid input, please try again")
        }
      }
      A[q %*% c(1, 3) + 1] = 2
    }
    if (l[B <- sum(A * E)])
      break
    # Player 2 has won
    t = g[B]
    C = G[B]
  }
  if (auto) {
    cat(c("D", 1:2)[1 + k[B] + 2 * l[B]])
  } else {
    cat("Outcome:", c("Draw", sprintf("Player %d wins", 1:2))[1 + k[B] + 2 * l[B]], "\n")
  }
  # Add beads back to matchbox
  M[x] = M[x <- cbind(z, j)] - k[B] - 1 + l[B]
  if (auto)
    M[x] = M[x <- cbind(Z, J)] - l[B] - 1 + k[B]
}

Çok zeki! Tabii ki, rotasyonlar zorlaştırıyor, ancak bot oynatıcısını da eklediğiniz için teşekkürler!
Geza Kerecsenyi
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.