Programcı Bulmacası: Bir oyun boyunca satranç tahtası durumunu kodlama


95

Kesinlikle bir soru değil, daha çok bir bilmece ...

Yıllar boyunca, yeni çalışanlarla birkaç teknik görüşmeye katıldım. Standart "X teknolojisini biliyor musunuz" sorularını sormanın yanı sıra, sorunlara nasıl yaklaştıkları konusunda da bir fikir edinmeye çalıştım. Tipik olarak, soruyu görüşmeden önceki gün e-posta ile gönderiyor ve ertesi gün bir çözüm bulmalarını bekliyorum.

Çoğunlukla sonuçlar oldukça ilginç - yanlış ama ilginç - ve kişi neden belirli bir yaklaşımı benimsediklerini açıklayabilirse yine de benim tavsiyemi alırdı.

Bu yüzden Stack Overflow izleyicileri için sorularımdan birini ortaya atmayı düşündüm.

Soru: Bir satranç oyununun (veya onun alt kümesinin) durumunu kodlamak için düşünebileceğiniz en alan verimli yol nedir? Yani, parçaları yasal olarak düzenlenmiş bir satranç tahtası verildiğinde, hem bu başlangıç ​​durumu hem de oyundaki oyuncular tarafından alınan sonraki tüm yasal hamleler kodlanır.

Cevap için kod gerekmez, sadece kullanacağınız algoritmanın bir açıklaması.

DÜZENLEME: Posterlerden birinin işaret ettiği gibi, hareketler arasındaki zaman aralığını dikkate almadım. Bunu da isteğe bağlı bir ekstra olarak hesaba katmaktan çekinmeyin :)

DÜZENLEME2: Sadece ek açıklama için ... Unutmayın, kodlayıcı / kod çözücü kural farkındadır. Gerçekten saklanması gereken şeyler oyuncunun seçimleridir - diğer her şeyin kodlayıcı / kod çözücü tarafından bilindiği varsayılabilir.

DÜZENLEME3: Burada bir kazanan seçmek zor olacak :) Çok sayıda harika yanıt!


4
Bir satranç oyununun başlangıç ​​durumu iyi tanımlanmış değil mi? Neden kodlanması gerekiyor? Sadece her dönüş (= hareket) arasındaki farkları kodlamanın yeterli olacağını düşünüyorum.
tanascius

1
Oyunun herhangi bir yasal ilk kurulumla başlayabileceğini varsayar (tıpkı gazetelerde bulabileceğiniz satranç oyunu bulmacalarında olduğu gibi).
Aaron Digulla

6
katı olmak için, tüm geçmiş pozisyonları da kodlamanız gerekecek, çünkü aynı pozisyon üç kez görünüyorsa, bu bir beraberliktir en.wikipedia.org/wiki/Threefold_repetition
flybywire

4
Öneri: Bunu insanların girişlerini program olarak gönderdiği gerçek bir yarışma haline getirin. Bir program girdi olarak bir satranç oyununu alır (bunun için bazı temel, insan tarafından okunabilir, optimize edilmemiş bir format tanımlayabilirsiniz) ve sıkıştırılmış oyunun çıktısını alır. Daha sonra, bir parametreyle, sıkıştırılmış oyunu alır ve eşleşmesi gereken orijinal girdiyi yeniden oluşturur.
Vilx

2
Daha da önemlisi, talimatları takip edemeyeceğinizi gösterirdi ... Çoğu ubercoder'ın bile bir noktada talimatları izlemesi gerekir. Aptalca bir uygulama olduğunu düşünmeme (ve söylememe) rağmen, bana bir şeyi belirli bir şekilde uygulamamın söylendiği durumlarla karşılaştım, sadece ortaya çıktığında yüzümde yumurta kaldı bu şekilde uygulanması için (bilmediğim ya da anlamadığım) çok iyi bir neden vardı.
Andrew Rollings

Yanıtlar:


132

Güncelleme: Bu konuyu o kadar sevdim ki Programlama Bulmacaları, Satranç Pozisyonları ve Huffman Kodlaması yazdım . Bunu okursanız, tam bir oyun durumunu saklamanın tek yolunun tam bir hamle listesi depolamak olduğunu belirledim . Nedenini okuyun. Bu yüzden parça düzeni için problemin biraz basitleştirilmiş bir versiyonunu kullanıyorum.

Sorun

Bu resim, satranç başlangıç ​​pozisyonunu göstermektedir. Satranç 8x8'lik bir tahtada, burada gösterildiği gibi 8 piyon, 2 kale, 2 at, 2 fil, 1 kraliçe ve 1 kraldan oluşan aynı 16 taştan oluşan bir setle başlayan her oyuncuyla gerçekleşir:

satranç pozisyonuna başlama

Pozisyonlar genellikle sütun için bir harf olarak ve ardından sıra numarası olarak kaydedilir, böylece Beyaz'ın veziri d1'dedir. Hareketler çoğunlukla belirsiz olmayan ve genellikle sadece gerekli minimum bilgileri belirten cebirsel gösterimde saklanır . Bu açılışı düşünün:

  1. e4 e5
  2. Af3 Ac6

bu da şu anlama gelir:

  1. Beyaz şahın piyonunu e2'den e4'e hareket ettirir (e4'e ulaşabilen tek taş dolayısıyla “e4 ”'tür);
  2. Siyah şahın piyonunu e7'den e5'e hareket ettirir;
  3. Beyaz, atı (N) f3'e hareket ettirir;
  4. Siyah, atı c6'ya taşır.

Yönetim kurulu şuna benzer:

erken açılış

Herhangi bir programcı için önemli bir yetenek , sorunu doğru ve net bir şekilde belirleyebilmektir .

Öyleyse eksik veya belirsiz olan nedir? Görünüşe göre çok fazla.

Board State vs Game State

Belirlemeniz gereken ilk şey, bir oyunun durumunu mu yoksa tahtadaki taşların konumunu mu depoladığınızdır. Sadece parçaların konumlarını kodlamak bir şeydir, ancak sorun "sonraki tüm yasal hamleler" diyor. Sorun, bu noktaya kadar olan hareketleri bilmek hakkında da hiçbir şey söylemiyor. Bu aslında açıklayacağım gibi bir problem.

Castling

Oyun şu şekilde ilerledi:

  1. e4 e5
  2. Af3 Ac6
  3. Fb5 a6
  4. Fa4 Fc5

Yönetim kurulu aşağıdaki gibi görünür:

daha sonra açılıyor

Beyazın rok yapma seçeneği vardır . Bunun gerekliliklerinden bir kısmı, şah ve ilgili kalenin asla hareket edemeyeceğidir, bu nedenle şahın veya her iki tarafın kalesinin hareket etmiş olup olmadığının depolanması gerekecek. Açıkçası başlangıç ​​pozisyonlarında değillerse, hareket etmişlerdir, aksi takdirde belirtilmesi gerekir.

Bu sorunu çözmek için kullanılabilecek birkaç strateji vardır.

İlk olarak, bu taşın hareket edip etmediğini belirtmek için fazladan 6 bit bilgi (her kale ve şah için 1) depolayabiliriz. Doğru parça varsa, bu altı kareden biri için yalnızca biraz depolayarak bunu kolaylaştırabiliriz. Alternatif olarak, her bir taşınmamış taşı başka bir taş türü olarak ele alabiliriz, böylece her iki taraftaki 6 taş türü (piyon, kale, at, fil, kraliçe ve şah) yerine 8 tane vardır (hareketsiz kale ve hareketsiz şah ekleyerek).

En Passant

Satrançta tuhaf ve sıklıkla ihmal edilen bir başka kural da En Passant'tır .

geçerken

Oyun ilerledi.

  1. e4 e5
  2. Af3 Ac6
  3. Fb5 a6
  4. Fa4 Fc5
  5. OO b5
  6. Fb3 b4
  7. c4

Siyahın b4'teki piyonu artık b4'teki piyonunu c4'teki Beyaz piyonu alarak c3'e taşıma seçeneğine sahiptir. Bu sadece ilk fırsatta olur, yani Siyah seçeneği geçerse bir sonraki hamleyi yapamaz. Yani bunu saklamamız gerekiyor.

Önceki hamleyi bilirsek, Geçer Tutma mümkün olup olmadığını kesinlikle cevaplayabiliriz. Alternatif olarak, 4. seviyedeki her bir piyonun ileriye doğru çift hareketle oraya yeni mi hareket ettiğini kaydedebiliriz. Ya da tahtadaki her olası En Passant pozisyonuna bakabilir ve bunun mümkün olup olmadığını gösteren bir bayrağa sahip olabiliriz.

Promosyon

rehin terfisi

Beyazın hamlesi. Beyaz h7'deki piyonunu h8'e hareket ettirirse, başka bir taşa terfi edebilir (ancak şaha değil). Zamanın% 99'u bir Kraliçe olarak terfi ettirilir, ancak bazen değildir, çünkü tipik olarak bu, aksi takdirde kazanırsanız bir çıkmaza neden olabilir. Bu şu şekilde yazılır:

  1. h8 = Q

Bu bizim sorunumuz için önemlidir çünkü bu, her iki tarafta da sabit sayıda parça olduğuna güvenemeyeceğimiz anlamına gelir. 8 piyonun da terfi etmesi halinde, bir tarafın 9 vezir, 10 kale, 10 fil veya 10 atla sonuçlanması tamamen mümkündür (ama inanılmaz derecede olası değildir).

Çıkmaz

Kazanamayacağınız bir pozisyondayken en iyi taktik, bir çıkmaza girmeye çalışmaktır . En olası varyant, yasal bir hamle yapamayacağınız yerdir (genellikle şahınızı kontrol altına aldığınızda herhangi bir hamle). Bu durumda bir çekiliş talep edebilirsiniz. Bunun karşılanması kolaydır.

İkinci varyant, üç kat tekrarlamadır . Bir oyunda aynı tahta pozisyonu üç kez meydana gelirse (veya bir sonraki hamlede üçüncü kez meydana gelirse), bir beraberlik talep edilebilir. Pozisyonların belirli bir sırada gerçekleşmesi gerekmez (yani, üç kez tekrarlanan aynı hamle dizisine sahip olmak zorunda değildir). Bu, sorunu büyük ölçüde karmaşıklaştırır çünkü önceki her tahta konumunu hatırlamanız gerekir. Bu, sorunun bir gereğiyse, sorunun olası tek çözümü, önceki her hareketi saklamaktır.

Son olarak, elli hamle kuralı var . Bir oyuncu, önceki elli ardışık hamlede hiç piyon hareket etmediyse ve hiçbir taş alınmadıysa, bir piyon hareket ettiğinden veya bir taş alındığından beri (ikisinin en sonuncusu) kaç hamle kaydetmemiz gerekir. 6 bit (0-63).

Kimin sırası?

Elbette sıra kimin olduğunu da bilmemiz gerekiyor ve bu ufak bir bilgi.

İki Sorun

Çıkmaz durum nedeniyle, oyun durumunu saklamanın tek uygulanabilir veya mantıklı yolu, bu konuma yol açan tüm hareketleri saklamaktır. Ben bu sorunu çözeceğim. Tahtanın durumu problemi şuna basitleştirilecektir: tüm parçaların mevcut konumunu tahtadaki rok, geçerken, çıkmaz koşulları ve kimin sırası olduğunu göz ardı ederek saklayın .

Parça düzeni iki yoldan biriyle genel olarak ele alınabilir: her karenin içeriğini saklayarak veya her bir parçanın konumunu saklayarak.

Basit İçerikler

Altı taş türü vardır (piyon, kale, at, fil, kraliçe ve şah). Her parça Beyaz veya Siyah olabilir, bu nedenle bir kare 12 olası parçadan birini içerebilir veya boş olabilir, bu nedenle 13 olasılık vardır. 13, 4 bitte (0-15) saklanabilir. Dolayısıyla en basit çözüm, her bir kare çarpı 64 kare veya 256 bit bilgi için 4 bit depolamaktır.

Bu yöntemin avantajı, manipülasyonun inanılmaz derecede kolay ve hızlı olmasıdır. Bu, depolama gereksinimlerini artırmadan 3 olasılık daha ekleyerek daha da uzatılabilir: son turda 2 boşluk hareket eden bir piyon, hareket etmemiş bir şah ve hareket etmemiş bir kale, çok şey sağlayacak daha önce bahsedilen sorunlardan.

Ama daha iyisini yapabiliriz.

Base 13 Kodlama

Yönetim kurulu pozisyonunu çok büyük bir sayı olarak düşünmek genellikle yararlıdır. Bu genellikle bilgisayar bilimlerinde yapılır. Örneğin, durdurma sorunu bir bilgisayar programını (haklı olarak) büyük bir sayı olarak ele alır.

İlk çözüm, konumu 64 basamaklı 16 tabanlı bir sayı olarak ele alır, ancak gösterildiği gibi, bu bilgide fazlalık vardır ("basamak" başına 3 kullanılmayan olasılıktır), böylece sayı alanını 64 taban 13 basamağa indirebiliriz. Elbette bu, taban 16'nın yapabileceği kadar verimli bir şekilde yapılamaz, ancak depolama gereksinimlerinden tasarruf sağlayacaktır (ve depolama alanını en aza indirmek hedefimizdir).

10 tabanında 234 sayısı 2 x 10 2 + 3 x 10 1 + 4 x 10 0'a eşdeğerdir .

16 tabanında 0xA50 sayısı 10 x 16 2 + 5 x 16 1 + 0 x 16 0 = 2640'a (ondalık) eşdeğerdir .

Biz p konumumuzu kodlamak Yani 0 x 13 63 + p 1 x 13 62 + ... + p 63 x 13 0 p ı kare içeriğini temsil i .

2 256 yaklaşık olarak 1.16e77'ye eşittir. 13 64 yaklaşık 1.96e71'e eşittir ve bu da 237 bit depolama alanı gerektirir. Yalnızca% 7,5'lik bu tasarruf, önemli ölçüde artan manipülasyon maliyetlerine neden olur.

Değişken Temel Kodlama

Yasal kurullarda belirli parçalar belirli karelerde görünmez. Örneğin, piyonlar birinci veya sekizinci sırada oluşamaz ve bu kareler için olasılıkları 11'e düşürür. Bu, olası tahtaları 11 16 x 13 48 = 1.35e70'e (yaklaşık olarak) düşürür ve 233 bit depolama alanı gerektirir.

Aslında, bu tür değerleri ondalık (veya ikili) arasında kodlamak ve kodunu çözmek biraz daha kıvrımlıdır, ancak güvenilir bir şekilde yapılabilir ve okuyucuya bir alıştırma olarak bırakılır.

Değişken Genişlikli Alfabeler

Önceki iki yöntemin her ikisi de sabit genişlikli alfabetik kodlama olarak tanımlanabilir . Alfabenin 11, 13 veya 16 üyesinden her biri başka bir değerle ikame edilir. Her "karakter" aynı genişliktedir ancak her karakterin eşit olasılıklı olmadığını düşündüğünüzde verimlilik artırılabilir.

Mors kodu

Mors alfabesini düşünün (yukarıda resmedilmiştir). Bir mesajdaki karakterler, bir dizi çizgi ve nokta olarak kodlanır. Bu çizgiler ve noktalar, onları sınırlandırmak için aralarında bir duraklama ile (tipik olarak) radyo üzerinden aktarılır.

E harfinin (İngilizce'deki en yaygın harf ) tek nokta, mümkün olan en kısa sıra, Z'nin (en az sıklıkta) iki çizgi ve iki bip sesi olduğuna dikkat edin.

Böyle bir şema, beklenen bir mesajın boyutunu önemli ölçüde azaltabilir, ancak rastgele bir karakter dizisinin boyutunu artırma maliyetine sahiptir.

Mors kodunun başka bir dahili özelliğe sahip olduğuna dikkat edilmelidir: kısa çizgiler üç nokta kadardır, bu nedenle yukarıdaki kod, tire kullanımını en aza indirmek için bu akılda tutularak oluşturulur. 1'ler ve 0'lar (yapı taşlarımız) bu soruna sahip olmadığından, kopyalamamız gereken bir özellik değil.

Son olarak, Mors alfabesinde iki tür dinlenme vardır. Noktaları ve tireleri birbirinden ayırmak için kısa bir dinlenme (bir noktanın uzunluğu) kullanılır. Karakterleri sınırlandırmak için daha uzun bir boşluk (tire uzunluğu) kullanılır.

Peki bu bizim sorunumuza nasıl uygulanabilir?

Huffman Kodlama

Huffman kodlaması adı verilen değişken uzunluklu kodlarla uğraşmak için bir algoritma var . Huffman kodlaması, daha yaygın sembollere daha kısa değerler atamak için tipik olarak sembollerin beklenen frekansını kullanan değişken uzunluklu bir kod ikamesi oluşturur.

Huffman kod ağacı

Yukarıdaki ağaçta, E harfi 000 olarak kodlanmıştır (veya sol-sol-sol) ve S 1011'dir. Bu kodlama şemasının açık olduğu açık olmalıdır .

Bu, Mors alfabesinden önemli bir ayrımdır. Mors kodu karakter ayırıcısına sahiptir, bu nedenle aksi takdirde belirsiz ikameler yapabilir (örneğin 4 nokta H veya 2 Is olabilir) ancak sadece 1'ler ve 0'lara sahibiz, bu yüzden bunun yerine kesin bir ikame seçeriz.

Aşağıda basit bir uygulama verilmiştir:

private static class Node {
  private final Node left;
  private final Node right;
  private final String label;
  private final int weight;

  private Node(String label, int weight) {
    this.left = null;
    this.right = null;
    this.label = label;
    this.weight = weight;
  }

  public Node(Node left, Node right) {
    this.left = left;
    this.right = right;
    label = "";
    weight = left.weight + right.weight;
  }

  public boolean isLeaf() { return left == null && right == null; }

  public Node getLeft() { return left; }

  public Node getRight() { return right; }

  public String getLabel() { return label; }

  public int getWeight() { return weight; }
}

statik verilerle:

private final static List<string> COLOURS;
private final static Map<string, integer> WEIGHTS;

static {
  List<string> list = new ArrayList<string>();
  list.add("White");
  list.add("Black");
  COLOURS = Collections.unmodifiableList(list);
  Map<string, integer> map = new HashMap<string, integer>();
  for (String colour : COLOURS) {
    map.put(colour + " " + "King", 1);
    map.put(colour + " " + "Queen";, 1);
    map.put(colour + " " + "Rook", 2);
    map.put(colour + " " + "Knight", 2);
    map.put(colour + " " + "Bishop";, 2);
    map.put(colour + " " + "Pawn", 8);
  }
  map.put("Empty", 32);
  WEIGHTS = Collections.unmodifiableMap(map);
}

ve:

private static class WeightComparator implements Comparator<node> {
  @Override
  public int compare(Node o1, Node o2) {
    if (o1.getWeight() == o2.getWeight()) {
      return 0;
    } else {
      return o1.getWeight() < o2.getWeight() ? -1 : 1;
    }
  }
}

private static class PathComparator implements Comparator<string> {
  @Override
  public int compare(String o1, String o2) {
    if (o1 == null) {
      return o2 == null ? 0 : -1;
    } else if (o2 == null) {
      return 1;
    } else {
      int length1 = o1.length();
      int length2 = o2.length();
      if (length1 == length2) {
        return o1.compareTo(o2);
      } else {
        return length1 < length2 ? -1 : 1;
      }
    }
  }
}

public static void main(String args[]) {
  PriorityQueue<node> queue = new PriorityQueue<node>(WEIGHTS.size(),
      new WeightComparator());
  for (Map.Entry<string, integer> entry : WEIGHTS.entrySet()) {
    queue.add(new Node(entry.getKey(), entry.getValue()));
  }
  while (queue.size() > 1) {
    Node first = queue.poll();
    Node second = queue.poll();
    queue.add(new Node(first, second));
  }
  Map<string, node> nodes = new TreeMap<string, node>(new PathComparator());
  addLeaves(nodes, queue.peek(), &quot;&quot;);
  for (Map.Entry<string, node> entry : nodes.entrySet()) {
    System.out.printf("%s %s%n", entry.getKey(), entry.getValue().getLabel());
  }
}

public static void addLeaves(Map<string, node> nodes, Node node, String prefix) {
  if (node != null) {
    addLeaves(nodes, node.getLeft(), prefix + "0");
    addLeaves(nodes, node.getRight(), prefix + "1");
    if (node.isLeaf()) {
      nodes.put(prefix, node);
    }
  }
}

Olası bir çıktı:

         White    Black
Empty          0 
Pawn       110      100
Rook     11111    11110
Knight   10110    10101
Bishop   10100    11100
Queen   111010   111011
King    101110   101111

Bir başlangıç ​​konumu için bu, 32 x 1 + 16 x 3 + 12 x 5 + 4 x 6 = 164 bite eşittir.

Durum Farkı

Başka bir olası yaklaşım, ilk yaklaşımı Huffman kodlamasıyla birleştirmektir. Bu, en çok beklenen satranç tahtalarının (rastgele oluşturulmuş olanlar yerine), en azından kısmen bir başlangıç ​​pozisyonuna benzememekten daha muhtemel olduğu varsayımına dayanmaktadır.

Yani yaptığınız şey, 256 bitlik bir başlangıç ​​konumu ile 256 bit mevcut kart konumunu XOR ve sonra bunu kodlamaktır (Huffman kodlaması veya, örneğin, bazı çalışma uzunluğu kodlama yöntemlerini kullanarak ). Açıkçası bu, başlamak için çok verimli olacaktır (64 0s muhtemelen 64 bite karşılık gelir), ancak oyun ilerledikçe gereken depolama alanı artar.

Parça Konumu

Bahsedildiği gibi, bu soruna saldırmanın başka bir yolu, oyuncunun sahip olduğu her parçanın konumunu saklamaktır. Bu, çoğu karenin boş olacağı oyun sonu konumlarında özellikle iyi çalışır (ancak Huffman kodlama yaklaşımında boş kareler zaten sadece 1 bit kullanır).

Her iki tarafta bir şah ve 0-15 diğer taş bulunur. Terfi nedeniyle, bu parçaların tam yapısı, başlangıç ​​pozisyonlarına göre sayıların maksimum olduğunu varsayamayacak kadar değişebilir.

Bunu bölmenin mantıklı yolu, iki Taraftan (Beyaz ve Siyah) oluşan bir Pozisyon kaydetmektir. Her Tarafta:

  • Bir kral: Konum için 6 bit;
  • Piyonları var: 1 (evet), 0 (hayır);
  • Evet ise, piyon sayısı: 3 bit (0-7 + 1 = 1-8);
  • Evet ise, her bir piyonun yeri kodlanmıştır: 45 bit (aşağıya bakın);
  • Piyon olmayanların sayısı: 4 bit (0-15);
  • Her taş için: tür (kraliçe, kale, at, fil için 2 bit) ve konum (6 bit)

Piyon konumuna gelince, piyonlar sadece 48 olası karede olabilir (diğerleri gibi 64 değil). Bu nedenle, piyon başına 6 bit kullanmanın kullanacağı ekstra 16 değeri israf etmemek daha iyidir. Yani 8 piyonunuz varsa, 28.179.280.429.056'ya eşit 48 8 olasılık vardır. Bu kadar çok değeri kodlamak için 45 bite ihtiyacınız var.

Bu, taraf başına 105 bit veya toplam 210 bittir. Başlangıç ​​pozisyonu, bu yöntem için en kötü durumdur ve parçaları kaldırdıkça önemli ölçüde daha iyi hale gelecektir.

Bu 48'den az olduğunu sivri dışarı olmalıdır 8 piyon hepsi ilk 48 olanaklarını, ikinci 47 ve benzeri bulunur aynı meydanda olamaz çünkü olasılıklar. 48 x 47 x… x 41 = 1.52e13 = 44 bit depolama.

Bunu, diğer taşların (diğer taraf dahil) işgal ettiği kareleri ortadan kaldırarak daha da geliştirebilirsiniz, böylece önce beyaz olmayan piyonları, sonra siyah olmayanları, sonra beyaz piyonları ve son olarak da siyah piyonları yerleştirebilirsiniz. Başlangıç ​​konumunda bu, depolama gereksinimlerini Beyaz için 44 bit ve Siyah için 42 bit'e düşürür.

Kombine Yaklaşımlar

Diğer bir olası optimizasyon, bu yaklaşımların her birinin kendi güçlü ve zayıf yönlerine sahip olmasıdır. Örneğin, en iyi 4'ü seçebilir ve ardından ilk iki bitte bir şema seçiciyi ve ardından şemaya özgü depolamayı kodlayabilirsiniz.

Bu kadar küçük ek yük ile, bu açık ara en iyi yaklaşım olacaktır.

Oyun Durumu

Bir pozisyondan çok oyun saklama sorununa dönüyorum . Üçlü tekrar nedeniyle, bu noktaya kadar meydana gelen hareketlerin listesini saklamamız gerekiyor.

Ek açıklamalar

Belirlemeniz gereken bir şey, sadece bir hamle listesi mi depoluyorsunuz yoksa oyuna açıklama mı veriyorsunuz? Satranç oyunlarına genellikle açıklama eklenir, örneğin:

  1. Fb5 !! Ac4?

Beyazın hareketi parlak olarak iki ünlem işareti ile işaretlenirken, Siyahın hareketi bir hata olarak görülüyor. Satranç noktalama işaretlerine bakın .

Ek olarak, hareketler açıklandıkça serbest metin de kaydetmeniz gerekebilir.

Hareketlerin yeterli olduğunu varsayıyorum, bu yüzden ek açıklama olmayacak.

Cebirsel Gösterim

Burada taşıma metnini basitçe saklayabiliriz ("e4", "Fxb5", vb.). Sonlandırıcı bir bayt dahil olmak üzere, hareket başına yaklaşık 6 bayta (48 bit) bakıyorsunuz (en kötü durum). Bu özellikle verimli değil.

Denenecek ikinci şey, başlangıç ​​konumunu (6 bit) ve bitiş konumunu (6 bit) saklamaktır, böylece hareket başına 12 bit. Bu önemli ölçüde daha iyi.

Alternatif olarak, mevcut konumdan tüm yasal hamleleri öngörülebilir ve belirleyici bir şekilde ve seçtiğimiz durumda belirleyebiliriz. Bu daha sonra yukarıda bahsedilen değişken temel kodlamaya geri döner. Beyaz ve Siyahın her birinin ilk hareketlerinde 20 olası hamlesi vardır, ikinci hamlede daha fazla vb.

Sonuç

Bu sorunun kesinlikle doğru bir cevabı yok. Yukarıdakilerin sadece birkaçı olduğu birçok olası yaklaşım vardır.

Bu ve benzeri sorunlardan hoşlandığım şey, herhangi bir programcı için kullanım modelini dikkate alma, gereksinimleri doğru bir şekilde belirleme ve köşe durumlarını düşünme gibi önemli yetenekler talep etmesidir.

Satranç Pozisyonu Antrenöründen ekran görüntüsü olarak alınan satranç pozisyonları .


3
ve sonucu daha sonra gzip ile sıkıştırın (başlıklar sonucu artırmazsa); ^)
Toad

Siyah veya Beyaz'ı belirtmek için alanı ikiye katlamanız gerekmez mi?
Daniel Elliott

5
İyi yazı. Küçük düzeltme: Rok atma, her bir rok yapma şekli için (beyaz ve siyah, şah kanadı ve vezir kanadı) 4 bit gerektirir, çünkü kaleler hareket etmiş ve sonra geri dönmüş olabilir. Biraz daha önemli: muhtemelen kimin hamlesi olduğunu eklemelisiniz. =)
A Rex

9
Bir şövalyeye terfi etmeye gelince, bunu bir kez yaptım. Gerçekten vahşi durum - benimle çiftleşmesine bir adım kaldı, onu durduramadım. Benim piyonumu görmezden gelmişti çünkü terfi ederken bir hamle gecikecekti. Keşke bir şövalyeye terfi ettiğimde ve onunla çiftleştiğimde kameram olsaydı!
Loren Pechtel

2
Makalenizde kalecilik, geçerken mevcudiyet vb. Konularını ele alan [FEN] [1] 'den bahsetmemesine şaşırdım. [1] en.wikipedia.org/wiki/FEN
Ross

48

Satranç oyunlarını insan tarafından okunabilir, standart bir formatta saklamak en iyisidir.

Taşınabilir Oyun Notasyonu (o her ne kadar standart bir başlangıç pozisyonu alır zorunda değildir ) ve sadece hamle listeler, dönüş açmak. Kompakt, insan tarafından okunabilir, standart bir format.

Örneğin

[Event "F/S Return Match"]
[Site "Belgrade, Serbia Yugoslavia|JUG"]
[Date "1992.11.04"]
[Round "29"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]

1. e4 e5 2. Nf3 Nc6 3. Bb5 {This opening is called the Ruy Lopez.} 3... a6
4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8  10. d4 Nbd7
11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5
Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6
23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5
hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5
35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6
Nf2 42. g4 Bd3 43. Re6 1/2-1/2

Küçültmek istiyorsanız, sıkıştırın . İş bitmiş!


23
Benim aldığım 2 olumsuz oyuna karşı savunmamda: 1) İstediğinizi yapıyor 2) thedailywtf.com/articles/riddle-me-an-interview.aspx testini geçiyor : "... çözebilecek bazı kişiler Bu bilmeceler tam da programcı olarak istemeyeceğiniz türden insanlar. Su deplasmanlı bir terazi / mavna yapan adamla çalışmak, limana bir 747 taksiyle ve ardından bunu kullanarak jumbo jeti ağırlıklandırmak ister misiniz? ilk etapta Boeing'i aramak yerine? " Röportajda size rastgele bir kodlama icat eden birini işe almazsınız çünkü onlar da bunu kendi kodlarında yapacaklardır.
Rob Grant

1
Sorun çözme tekniklerini öğrenmek için onlardan özellikle bir problemi çözmelerini istiyorsam, diğer konuları başka sorularla ele alacağımı varsayabilirsiniz ...
Andrew Rollings

7
@reinier: Bilgi yoğunluğu sorunları hakkında tamamen bilgisiz olduğumu söylemiyorum (cevabımı yetersizliğin bir kabulü olarak yanlış teşhis ettiniz). Elbette, mevcut bir veri depolama standardını kodlayan ve kendi araçlarını döndürmek yerine uygun mevcut araçları kullanmanın iyi bir fikir olabileceğini anlayan kişiyi işe almak istiyorsunuz - "The Wheel 2.0'ı icat ettik! Artık daha da yuvarlak!" Garip bir şekilde kütüphane işlevlerini kullanmanın bir zayıflık işareti olduğunu düşünen kişiyi kesinlikle işe almak istemezsiniz.
Rob Grant

18
Bu kesinlikle bir röportajda bu soruya ilk cevabım olacaktır. İlk içgüdünüzün hazır bir çözüm aramak olduğunu göstermek istiyorsunuz. Görüşmeci size kendi başınıza neler bulabileceğinizi duymak istediklerini söylerse, o zaman biraz paketleme çözümüne geçebilirsiniz.
Bill the Lizard

2
Bu konuda Robert ile birlikteyim - mevcut çözüm pratik, insanlar tarafından okunabilir ve yeterince kompakt. Bunların tümü, bunları çözmek için karmaşık algoritmalar içeren özel süper paketlenmiş çözümlere kıyasla BÜYÜK becerilerdir. Mülakatla ilgiliyse, kesinlikle pratik yönü de düşünürdüm! Kaç kez gerçekten zeki insanların hiper karmaşık pratik olmayan çözümler ürettiklerine şaşıracaksınız. Genellikle kafalarındaki karmaşıklığın üstesinden gelebilmelerine atfedilir, ama sonra - peki ya geri
kalanımız

15

Harika bulmaca!

Çoğu insanın her bir parçanın konumunu sakladığını görüyorum. Daha basit bir yaklaşım benimsemeye ve her karenin içeriğini saklamaya ne dersiniz ? Bu, promosyon ve yakalanan parçaları otomatik olarak halleder.

Ve Huffman kodlamasına izin verir . Aslında, tahtadaki taşların başlangıçtaki frekansı bunun için neredeyse mükemmel: karelerin yarısı boş, kalan karelerin yarısı piyon, vb.

Her bir parçanın frekansını göz önünde bulundurarak, kağıt üzerine bir Huffman ağacı inşa ettim, bunu burada tekrar etmeyeceğim. Sonuç, crenk anlamına gelir (beyaz = 0, siyah = 1):

  • 0 boş kareler için
  • Piyon için 1c0
  • Kale için 1c100
  • Şövalye için 1c101
  • Fil için 1c110
  • Kraliçe için 1c1110
  • Kral için 1c1111

Başlangıçtaki tüm yönetim kurulu için, bizde

  • boş kareler: 32 * 1 bit = 32 bit
  • piyonlar: 16 * 3 bit = 48 bit
  • kaleler / şövalyeler / filler: 12 * 5 bit = 60 bit
  • kraliçeler / krallar: 4 * 6 bit = 24 bit

Toplam: İlk kart durumu için 164 bit . Şu anda en yüksek oyu alan cevabın 235 bitinden önemli ölçüde daha az. Ve sadece oyun ilerledikçe küçülecektir (bir terfi sonrası hariç).

Sadece tahtadaki parçaların konumuna baktım; ek durum (sırası, kim rok attı, geçerken, tekrarlayan hamleler vb.) ayrı ayrı kodlanmalıdır. Belki başka bir 16 bit daha, yani tüm oyun durumu için 180 bit . Olası optimizasyonlar:

  • Daha seyrek olan parçaları dışarıda bırakıp konumlarını ayrı ayrı saklamak. Ancak bu yardımcı olmaz ... şah ve veziri boş bir kareyle değiştirmek 5 bit kaydeder, bu tam olarak konumlarını başka bir şekilde kodlamanız gereken 5 bittir.
  • Arka sıralar için farklı bir Huffman tablosu kullanılarak "arka sıradaki hiçbir piyon" kolayca kodlanamaz, ancak bunun çok yardımcı olduğundan şüpheliyim. Muhtemelen yine aynı Huffman ağacına sahip olacaksınız.
  • "Bir beyaz, bir siyah fil" cbiti olmayan ekstra semboller eklenerek kodlanabilir ve bunlar daha sonra filin bulunduğu kareden çıkarılabilir. (Fillere terfi ettirilen piyonlar bu düzeni bozuyor ...)
  • Boş karelerin tekrarları, örneğin "arka arkaya 2 boş kare" ve "arka arkaya 4 boş kare" için ekstra semboller eklenerek sayı uzunluğu kodlanabilir. Ama bunların sıklığını tahmin etmek o kadar kolay değil ve eğer yanlış yaparsanız, yardımcı olmaktan çok canınızı yakacaktır.

Banka sıralarındaki hiçbir piyon biraz tasarruf sağlamaz - diğer tüm kalıplardan 3. biti kesebilirsiniz. Böylece, aslında bir banka sıralamasında parça başına bir bit tasarruf edeceksiniz.
Loren Pechtel

2
64 karenin her biri için ayrı bir Huffman ağacı yapabilirsiniz, çünkü bazılarının bazı parçaları diğerlerinden daha sık olabilir.
Claudiu

9

Gerçekten büyük başvuru tablosu yaklaşımı

Konum - 18 bayt
Tahmini geçerli pozisyon sayısı 10'dur 43
Basitçe hepsini sayın ve pozisyon sadece 143 bit olarak saklanabilir. Bir sonraki hangi tarafın oynanacağını belirtmek için 1 bit daha gerekli

Numaralandırma elbette pratik değildir, ancak bu en az 144 bit gerektiğini gösterir.

Hareketler - 1 bayt
Her pozisyon için genellikle yaklaşık 30-40 yasal hareket vardır, ancak sayı 218'e kadar çıkabilir Her pozisyon için tüm yasal hamleleri sıralayalım. Artık her hareket bir bayt olarak kodlanabilir.

İstifa etmeyi temsil edecek 0xFF gibi özel hareketler için hala bolca yerimiz var.


3
"Bir satranç oyununun durumunu kodlamak için aklınıza gelebilecek en alan verimli yol" gereksiniminin tam kalbine gelince - Bir şeyi ezmek bir sözlükten daha iyi değildir ve buna sinek dahildir.
Andrew

1
Böyle bir sözlüğü oluşturmanın ne kadar süreceği konusunda ilginç bir bağlantı buldum :) ioannis.virtualcomposer2000.com/math/EveryChess.html
Andrew Rollings

Shannons tahmini biraz modası geçmiş :-) Ne promosyonlar ne de yakalamalar içermedi, bu da sayıyı makul bir miktarda artırdı. 5x10 ^ 52'lik bir üst sınır Victor Allis 1994 tarafından verildi.
Gunther Piez

Şüphesiz, değişken uzunluk kodlamasıyla sadece ortalama en az 10 ^ 43'tür. Daha fazla pozisyona yönelik önyargılı bir kodlama, özellikle pozisyonların çoğu imkansız olduğu için bunu azaltmalıdır.
Phil H

EveryChess bağlantısı artık 'satılık', archive.org bağlantısı: web.archive.org/web/20120303094654/http://…
oPless

4

Ortalama durum için optimize etmek ilgi katar kötü durum yerine, insanlar tarafından oynanan tipik oyunlar boyutunu . (Sorun ifadesi hangisi olduğunu söylemez; çoğu yanıt en kötü durumu varsayar.)

Hareket sırası için, iyi bir satranç motorunun her pozisyondan hamle oluşturmasını sağlayın; kalitelerine göre sıralı k olası hamle listesi oluşturacaktır. İnsanlar genellikle rastgele hareketlerden daha sık iyi hareketler seçerler, bu nedenle listedeki her konumdan insanların 'iyi' olan bir hamleyi seçme olasılığına bir eşleme öğrenmemiz gerekir. Bu olasılıkları kullanarak (bazı internet satranç veritabanlarından alınan oyunlara dayalı olarak), hamleleri aritmetik kodlama ile kodlayın . (Kod çözücü aynı satranç motorunu ve eşlemeyi kullanmalıdır.)

Başlangıç ​​pozisyonu için ralu'nun yaklaşımı işe yarayacaktır. Seçimleri olasılığa göre ağırlıklandırmanın bir yolunu bulmuş olsaydık, orada aritmetik kodlamayla da düzeltebilirdik - örneğin parçalar genellikle rastgele değil, birbirini savunan konfigürasyonlarda görünür. Bu bilgiyi dahil etmenin kolay bir yolunu görmek daha zor. Bir fikir: standart açılış konumundan başlayarak ve istenen kartta biten bir sıra bularak yukarıdaki hareket kodlamasına geri dönün. (A * aramasını, taşların son konumlarına olan mesafelerinin toplamına eşit olan sezgisel bir mesafe ile veya bu çizgiler boyunca bir şeyle deneyebilirsiniz.) Bu, satranç oynamanın avantajlarından yararlanarak, hareket sırasını aşırı belirlemekten ve verimlilikten bir miktar verimsizliğin ticaretini yapar. bilgi.

Ayrıca, gerçek bir derlemeden bazı istatistikler toplamadan, bunun ortalama durum karmaşıklığında size ne kadar tasarruf sağlayacağını tahmin etmek de biraz zor. Ancak tüm hareketlerin eşit derecede muhtemel olduğu başlangıç ​​noktası, sanırım buradaki önerilerin çoğunu zaten yenerdi: aritmetik kodlama, hareket başına tam sayı bit sayısına ihtiyaç duymaz.


Bu bilgiyi havuza kaydetme zorunluluğu O (n), düzenlenmiş cevabıma bakın.
Luka Rahne

ralu, ne dediğinden emin değilim, ama bir dizi hamle temsilinin en kötü durumda optimal alanı kullandığını kastediyorsan, o zaman bununla çelişmem. Buradaki fikir, bazı hareketlerin diğerlerinden daha muhtemel olmasından yararlanmaktır.
Darius Bacon

Daha benzer pozisyonları bulmak için ihtiyacınız olan tek şey, belirli bir pozisyondaki mevcut hareketi belirleyen şekilde sıralayan deterministik (ve güçlü) satranç motorunu kullanmaktır.
Luka Rahne

4

Bir başlangıç ​​konumu kodlandıktan sonra adımları kodlamanın bir alt problemine saldırmak. Yaklaşım, adımlardan oluşan bir "bağlantılı liste" oluşturmaktır.

Oyundaki her adım "eski pozisyon-> yeni pozisyon" çifti olarak kodlanmıştır. Satranç oyununun başlangıcındaki başlangıç ​​konumunu biliyorsunuz; bağlantılı adımlar listesini geçerek, X hareketinden sonraki duruma geçebilirsiniz.

Her adımı kodlamak için, başlangıç ​​konumunu kodlamak için 64 değere (tahtadaki 64 kare için 6 bit - 8x8 kare) ve son konum için 6 bit'e ihtiyacınız vardır. Her bir tarafın 1 hareketi için 16 bit.

Belirli bir oyunu kodlamanın alacağı alan miktarı, hamle sayısıyla orantılıdır:

10 x (beyaz hamle sayısı + siyah hamle sayısı) bit.

GÜNCELLEME: terfi piyonlar ile olası komplikasyon. Piyonun neye yükseltildiğini belirtebilmeli - özel bitlere ihtiyaç duyabilir (piyon terfisi son derece nadir olduğundan, yer kazanmak için bunun için gri kod kullanılır).

GÜNCELLEME 2: Bitiş konumunun tam koordinatlarını kodlamanız gerekmez. Çoğu durumda, taşınan parça en fazla X yere taşınamaz. Örneğin, bir piyon herhangi bir noktada maksimum 3 hamle seçeneğine sahip olabilir. Her parça türü için maksimum hareket sayısının farkına vararak, "hedef" kodlamasında bitleri kaydedebiliriz.

Pawn: 
   - 2 options for movement (e2e3 or e2e4) + 2 options for taking = 4 options to encode
   - 12 options for promotions - 4 promotions (knight, biship, rook, queen) times 3 squares (because you can take a piece on the last row and promote the pawn at the same time)
   - Total of 16 options, 4 bits
Knight: 8 options, 3 bits
Bishop: 4 bits
Rook: 4 bits
King: 3 bits
Queen: 5 bits

Böylece siyah veya beyazın hareket başına uzamsal karmaşıklığı

İlk konum için 6 bit + (taşınan şeyin türüne bağlı olarak değişken bit sayısı).


Az önce güncellenmiş, 128 kombinasyonu kastetmiştim - açıkça 128 bitten az :) :)
Alex Weinstein

1
Oyun durumu, hamle ile aynı şey değildir. Verilen herhangi bir konum bir tepe veya düğüm olarak düşünülebilir ve yasal bir hareket, (yönlendirilmiş döngüsel olmayan) bir grafik oluşturan yönlendirilmiş bir kenar veya ok olarak düşünülebilir.
Shaggy Kurbağa

Neden olumsuz oy aldığından emin değilim - insanların güncellenen fikir hakkındaki fikirlerini duymak isterim.
Alex Weinstein

1
Bu, mantığınızı etkilemez, ancak küçük bir düzeltme: bir piyon, terfiler hariç dört hamle veya terfiler dahil 12 hamle içerebilir. E2: e3, e4, exd3, exf3'teki örnek piyon. E7'deki örnek piyon: e8Q, e8N, e8R, e8B, exd8Q, exd8N, exd8R, exd8B, exf8Q, exf8N, exf8R, exf8B.
A. Rex

1
Küçük bir sorun - 5 bit yalnızca 32 değeri kodlar. Tahtadaki herhangi bir kareyi belirtmek için 6 bite ihtiyacınız vardır.
Chris Dodd

4

Dün gece bu soruyu gördüm ve ilgimi çekti, bu yüzden yatakta oturup çözümler düşündüm. Son cevabım aslında int3'lere oldukça benzer.

Temel çözüm

Standart bir satranç oyunu varsayarsak ve kuralları kodlamadığınızı varsayarsak (Beyaz her zaman önce gelir gibi), o zaman sadece her parçanın yaptığı hamleleri kodlayarak çok fazla tasarruf edebilirsiniz.

Toplamda 32 parça var ancak her harekette hangi rengin hareket ettiğini biliyorsunuz, bu yüzden endişelenmeniz gereken sadece 16 kare var, bu da bu dönüşü hangi taş için 4 bittir .

Her parçanın, bir şekilde sıralayacağınız sınırlı bir hamle seti vardır.

  • Piyon: 4 seçenek, 2 bit (1 adım ileri, 2 adım ileri, 1 çapraz)
  • Kale: 14 seçenek, 4 bit (her yönde maksimum 7)
  • Piskopos: 13 seçenek, 4 bit (bir köşegende 7'ye sahipseniz, diğerinde yalnızca 6'ya sahipsiniz)
  • Şövalye: 8 seçenek, 3 bit
  • Kraliçe: 27 seçenek, 5 bit (Kale + Piskopos)
  • King: 9 seçenek, 4 bit (8 tek adımlı hareket, artı rok seçeneği)

Terfi için seçilebilecek 4 parça var (Kale, Piskopos, Şövalye, Kraliçe), bu nedenle bunu belirtmek için 2 bit ekleyeceğiz . Diğer tüm kuralların otomatik olarak ele alındığını düşünüyorum (örneğin geçerken).

Diğer optimizasyonlar

İlk olarak, bir renkten 8 parça yakalandıktan sonra, parça kodlamasını 3 bit'e, ardından 4 parça için 2 bit'e vb.

Ancak ana optimizasyon , oyunun her noktasında yalnızca olası hareketleri numaralandırmaktır . Bir Piyonun hareketlerini sırasıyla {00, 01, 10, 11}1 adım ileri, 2 adım ileri, sol çapraz ve sağ çapraz olarak kaydettiğimizi varsayalım . Bazı hareketler mümkün değilse, onları bu dönüş için kodlamadan çıkarabiliriz.

Oyunun durumunu her aşamada biliyoruz (tüm hareketleri takip ederek), bu nedenle hangi parçanın hareket edeceğini okuduktan sonra, kaç bit okumamız gerektiğini her zaman belirleyebiliriz. Bir piyonun bu noktada tek hareketinin çapraz olarak sağa tutulduğunu veya bir piyon ileriye doğru hareket ettiğini fark edersek, sadece 1 bit okumayı biliyoruz.

Kısacası, her bir parça için yukarıda listelenen bit depolama alanı yalnızca maksimumdur . Neredeyse her hareketin daha az seçeneği ve genellikle daha az biti olacaktır.


4

Her pozisyonda tüm olası hareketlerin sayısını alın.

sonraki hareket şu şekilde üretilir:

index_current_move =n % num_of_moves //this is best space efficiency
n=n/num_of_moves

Rastgele oluşturulmuş oyunu depolamak için kanıtlanabilecek en iyi alan verimliliği ve 30-40 olası hareketiniz olduğundan ortalama olarak yaklaşık 5 bit / harekete ihtiyaç duyar. Depolamayı birleştirmek sadece ters sırada n üretiyor.

Büyük fazlalık nedeniyle konumu kaydetmenin kırılması daha zordur. (Bir alan için gemide en fazla 9 kraliçe olabilir, ancak bu durumda piyonlar ve tahtada zıt renkli karelerdeyse filler yoktur) ancak genellikle aynı taşların kombinasyonunu kalan kareler üzerinde depolamak gibidir.)

DÜZENLE:

Hareketleri kaydetmenin amacı, sadece hareket indeksini saklamaktır. Şc1-c2'yi depolamak ve bu bilgiyi azaltmaya çalışmak yerine, yalnızca deterministik hareket oluşturucudan (konum) üretilen hareket indeksini eklemeliyiz.

Her harekette boyut bilgisi ekliyoruz

num_of_moves = get_number_of_possible_moves(postion) ;

havuzda ve bu sayı azaltılamaz

bilgi havuzu oluşturmak

n=n*num_of_moves+ index_current_move

ekstra

Son konumda yalnızca bir hareket varsa, daha önce yapılan zorunlu hareket sayısı olarak kaydedin. Örnek: Eğer başlangıç ​​pozisyonunda her iki taraf için 1 zorunlu hareket varsa (2 hamle) ve bunu tek hamleli oyun olarak kaydetmek istiyorsak, 1'i havuz n'de saklayın.

bilgi havuzunda saklama örneği

Başlangıç ​​pozisyonlarımızı bildiğimizi ve 3 hamle yaptığımızı varsayalım.

İlk hamlede 5 hamle var ve hareket endeksi 4'ü alıyoruz. İkinci hamlede 6 hamle var ve 3. pozisyon indeksini alıyoruz ve 3. hamlede o taraf için 7 hareket var ve o hareket indeksini seçmeyi seçti 2.

Vektör formu; dizin = [4,3,2] n_moves = [5,6,7]

Bu bilgiyi geriye doğru kodluyoruz, bu nedenle n = 4 + 5 * (3 + 6 * (2)) = 79 (7 ile çarpma gerekmez)

Bunu nasıl çözebilirim? İlk önce pozisyonumuz var ve 5 hamle olduğunu öğreniyoruz. Yani

index=79%5=4
n=79/5=15; //no remainder

4. hamle indeksini alıp pozisyonu tekrar inceliyoruz ve bu noktadan 6 olası hamle olduğunu öğreniyoruz.

index=15%6=3
n=15/6=2

Ve bizi olası 7 hamle ile bir konuma götüren 3. hamle indeksini alıyoruz.

index=2%7=2
n=2/7=0

Son hamle endeksi 2'yi yapıyoruz ve son konuma ulaşıyoruz.

Gördüğünüz gibi zaman karmaşıklığı O (n) ve uzay karmaşıklığı O (n). Düzenleme: Zaman karmaşıklığı aslında O (n ^ 2) çünkü çarptığınız sayı artıyor, ancak 10.000 hamleye kadar olan oyunları saklamada sorun olmamalı.


pozisyon kaydetme

Optimuma yakın yapılabilir.

Bilgileri öğrendiğimizde ve sakladığımız zaman biraz daha konuşayım. Genel fikir, fazlalığı azaltmaktır (bunun hakkında daha sonra konuşacağım). Terfi ve alma olmadığını varsayalım, bu nedenle her tarafta 8 piyon, 2 kale, 2 at, 2 fil, 1 kral ve 1 vezir vardır.

Neyi kurtarmalıyız: 1. Her barışın konumu 2. Rok atma olanakları 3. Geçme olasılıkları 4. Harekete sahip olan taraf

Farz edelim ki her parça herhangi bir yerde durabilir ama aynı yerde 2 parça olamaz. Gemide aynı renkte 8 piyonun düzenlenebileceği yol sayısı 32 bit olan C (64/8) (iki terimli), ardından 2 kale 2R-> C (56/2), 2B -> C (54/2) , 2N-> C (52/2), 1Q-> C (50/1), 1K -> C (49/1) ve diğer site için aynı ancak 8P -> C (48/8) ile başlayarak vb. .

Bunu her iki bölge için çarparsak bize 46347266955878096411920459823285670400000 numarayı elde ederiz, bu yaklaşık 142 bittir, olası bir geçiş piyonu için 8 eklememiz gerekir (geçerken piyon 8 yerden birinde olabilir), 16 (4 bit) rok atma sınırlamaları için ve hareket eden site için bir bit. 142 + 3 + 4 + 1 = 150 bit ile sonuçlanırız

Ama şimdi tahtada 32 parça ile ve hiç almayarak fazlalık avına çıkalım.

  1. hem siyah hem de beyaz piyonlar aynı sütunda ve birbirlerine bakıyorlar. Her piyon başka bir piyonla karşı karşıyadır, yani beyaz piyon en fazla 6. sırada olabilir. Bu bize bilgiyi 56 bit azaltan C (64/8) * C (48/8) yerine 8 * C (6/2) getirir.

  2. Rok yapma olasılığı da gereksizdir. Kaleler başlangıç ​​noktasında değilse, o kale için rok yapma ihtimali yoktur. Böylece, bu kaleyi roketmek mümkünse ekstra bilgi almak için gemiye 4 kare ekleyebilir ve 4 rok bitini kaldırabiliriz. Yani C (56/2) * C (40/2) * 16 yerine C (58/2) * C (42/2) var ve 3.76 bit kaybettik (neredeyse 4 bitin tamamı)

  3. en-passant: 8 geçerken alma olasılığından birini sakladığımızda, siyah piyonun konumunu biliriz ve bilgilendirici redindancy'yi azaltırız (eğer bu beyaz hareketse ve 3. piyon en-passant'a sahipse, bu siyah piyonun c5'te olduğu ve beyaz piyonun da olduğu anlamına gelir. c2, c3 veya c4) böylece C (6/2) yerine 3 bit elde ettik ve 2.3 bit kaybettik. Beyaz geçiş sayısını da yapılabilecek taraf olarak (3 olasılık -> sol, sağ, her ikisi) kaydedersek, fazlalığı bir miktar azaltırız ve piyonun geçerken alabilecek olasılığını biliriz. (örneğin önceki geçerken geçen örnekten c5'teki siyah beyazdan solda, sağda veya her ikisinde de olabilir. Eğer bir sahadaysa 2 * 3'ümüz var (psissibilitleri depolamak için 3 ve siyah piyon için 7. veya 6. sıradaki 2 olası hamle ) C (6/2) olarak yazılır ve 1.3 bit azaltılır ve eğer her iki tarafta da 4.2 bit düşürürsek 2.3 + 1.3 = 3 azaltabiliriz.

  4. piskoposlar: bisoplar yalnızca opostit kareler üzerinde olabilir, bu her bölge için fazlalığı 1 bit azaltır.

Özetleyecek olursak, eğer hiç alım yoksa satranç pozisyonunu saklamak için 150-56-4-3.6-2 = 85 bit'e ihtiyacımız var

Ve muhtemelen hesaba katılmış alımlar ve promosyonlar varsa daha fazlası değil (ancak birileri bu uzun gönderiyi yararlı bulacaksa daha sonra bunu yazacağım)


İlginç bir yaklaşım. Biraz daha ayrıntı ekleyin :)
Andrew Rollings

Ayrıca pozisyon kaydetme yaklaşımı da ekledim. Alım yapmadan pozisyonlarda 85 bit'e düştüm ve ne kadar ileri gitmek mümkünse iyi bir illüzyon. Bence en iyi fikir, neredeyse tüm 4 bitin gereksiz olduğu yerlerde rok yapma olasılıklarından tasarruf etmektir.
Luka Rahne

3

Çoğu kişi pano durumunu kodluyor, ancak hareketlerle ilgili olarak .. İşte bir bit kodlama açıklaması.

Parça başına bit sayısı:

  • Parça Kimliği: Her taraf için 16 parçayı tanımlamak için maksimum 4 bit. Beyaz / siyah çıkarılabilir. Parçalarda tanımlı bir sipariş verin. Parçaların sayısı ilgili ikisinin gücünün altına düştüğü için, kalan parçaları tanımlamak için daha az bit kullanın.
  • Piyon: İlk hamlede 3 olasılık, yani +2 bit (bir veya iki kare ileri, geçerken) Sonraki hareketler iki sayı ilerlemeye izin vermez, bu nedenle +1 bit yeterlidir. Terfi, kod çözme sürecinde, piyonun son sıraya ne zaman ulaştığına dikkat edilerek çıkarılabilir. Piyonun terfi ettiği biliniyorsa, kod çözücü 4 ana taştan hangisine terfi ettirildiğini gösteren 2 bit daha bekleyecektir.
  • Bishop: Kullanılan çapraz için +1 bit, Köşegen boyunca mesafe için +4 bit'e kadar (16 olasılık). Kod çözücü, parçanın bu köşegen boyunca hareket edebileceği maksimum olası mesafeyi çıkarabilir, bu nedenle daha kısa bir köşegen ise daha az bit kullanın.
  • At: 8 olası hamle, +3 bit
  • Kale: Yatay / dikey için +1 bit, çizgi boyunca mesafe için +4 bit.
  • Şah: 8 olası hamle, +3 bit. Rok atmayı 'imkansız' bir hareketle belirtin - rok atma yalnızca şah birinci sıradayken mümkün olduğundan, bu hamleyi şahı 'geri' hareket ettirme talimatı ile kodlayın - yani tahtanın dışına.
  • Kraliçe: 8 olası yön, + 3 bit. Çizgi / diyagonal boyunca mesafe için +4'e kadar daha fazla bit (filin durumunda olduğu gibi köşegen daha kısaysa daha az)

Tüm parçaların tahtada olduğunu varsayarsak, bunlar hamle başına bitlerdir: Piyon - ilk hamlede 6, ardından 5 bit. 7 terfi ederse. Piskopos: 9 bit (maks.), Şövalye: 7, Kale: 9, Şah: 7, Kız: 11 (maks.).


Parçayı tanımlamak için 32 bit ??? Sanırım 5 (32 parça) demek istedin. Veya bir 'bitiş' durumunu kodlamanız gerekirse 6
Toad

Bir piyon ayrıca kale, at veya fil olarak terfi ettirilebilir. Bu, Çıkmazdan kaçınmak veya çatışmayı önlemek için yaygındır.
Kobi

Bu, mantığınızı etkilemez, ancak küçük bir düzeltme: bir piyon, terfiler hariç dört hamle veya terfiler dahil 12 hamle içerebilir. E2: e3, e4, exd3, exf3'teki örnek piyon. E7'deki örnek piyon: e8Q, e8N, e8R, e8B, exd8Q, exd8N, exd8R, exd8B, exf8Q, exf8N, exf8R, exf8B.
A. Rex

Belki yanlış okuyorum, ama bir piyon ilk hareketinde geçerken yapamaz. Aslında oyun kurallarında olduğu için özel bir "geçerken alma" notasyonuna ihtiyacınız yok - bu sadece çapraz bir hareket olacak. İlk hareket 4 seçenekten biridir ve sonraki hamleler en fazla 3 seçenektir.
DisgruntledGoat

3

Sorun, tipik satranç oyunları için en verimli olan bir kodlama mı yoksa en kısa en kötü durum kodlamasına sahip olan mı?

İkincisi için, en verimli yol aynı zamanda en opak olanıdır: olası tüm çiftlerin (ilk tahta, yasal hamle sırası) bir numaralandırmasını oluşturmak, üç kez tekrarlanan pozisyonda çek ve en fazla Son piyon hamle veya ele geçirme kurallarından bu yana elli hamle özyinelemelidir. Daha sonra, bu sonlu dizideki bir konumun indeksi, en kısa en kötü durum kodlamasını verir, ancak aynı zamanda tipik durumlar için eşit derecede uzun kodlamayı verir ve tahmin ediyorum, hesaplaması çok pahalıdır. Mümkün olan en uzun satranç oyununun 5000 hamleden fazla olması beklenir, tipik olarak her oyuncu için her pozisyonda 20-30 hamle bulunur (çok az taş kaldığında daha az olsa da) - bu, bu kodlama için gerekli olan 40000 bit gibi bir şey verir.

Numaralandırma fikri, Henk Holterman'ın yukarıdaki hareketleri kodlama önerisinde açıklandığı gibi daha izlenebilir bir çözüm sağlamak için uygulanabilir. Önerim: asgari düzeyde değil, yukarıda incelediğim örneklerden daha kısa ve makul şekilde izlenebilir:

  1. Hangi karelerin işgal edildiğini temsil etmek için 64 bit (doluluk matrisi), artı her işgal edilen karede hangi parçaların bulunduğu listesi (piyonlar için 3 bit ve diğer parçalar için 4 bit olabilir): bu, başlangıç ​​konumu için 190 bit verir. Gemide 32'den fazla parça olamayacağından, doluluk matrisinin kodlaması fazladır ve bu nedenle ortak pano pozisyonları gibi bir şey, örneğin 33 set bit artı ortak pano listesinden pano indeksi olarak kodlanabilir.

  2. İlk hareketi kimin yaptığını söylemek için 1 bit

  3. Henk'in önerisine göre kod hareketleri: tipik olarak beyaz / siyah hareket çifti başına 10 bit, ancak bir oyuncunun alternatif hamlesi olmadığında bazı hareketler 0 bit alacaktır.

Bu, tipik bir 30 hamlelik oyunu kodlamak için 490 bit önermektedir ve tipik oyunlar için oldukça verimli bir temsil olacaktır.

Son piyon hamle veya ele geçirme kurallarından bu yana üç kez tekrarlanan pozisyonda çekilişi ve elliden fazla hamleyi kodlamadan önce: son hamleleri son piyon hareketine veya ele geçirmeye kodlarsanız, o zaman siz Bu kuralların geçerli olup olmadığına karar vermek için yeterli bilgiye sahip olun: tüm oyun geçmişine gerek yok.


Çok sayıda oyun seçeceğimi ve sonuçların ortalamasını alacağımı varsayalım.
Andrew Rollings

3

Bir kart üzerindeki pozisyon 7 bit olarak tanımlanabilir (0-63 ve 1 değer artık kartta olmadığını belirtir). Yani panodaki her parça için nerede olduğunu belirtin.

32 parça * 7 bit = 224 bit

DÜZENLEME: Cadrian'ın da belirttiği gibi ... Ayrıca 'terfi piyondan kraliçeye' vakasına da sahibiz. Hangi piyonun terfi ettiğini belirtmek için sonuna fazladan bit eklememizi öneririm.

Dolayısıyla, terfi ettirilen her piyon için, terfi ettirilen piyonun endeksini gösteren 5 bitlik 224 biti ve listenin sonuysa 11111'i takip ederiz.

Bu nedenle minimum durum (promosyon yok) 224 bit + 5'tir (promosyon yok). Her yükseltilmiş piyon için 5 bit ekleyin.

DÜZENLEME: Tüylü kurbağanın da belirttiği gibi, sıranın kimin olduğunu belirtmek için sonunda başka bir parçaya ihtiyacımız var; ^)


ve sonucu daha sonra gzip ile sıkıştırın (başlıklar sonucu artırmazsa); ^)
Toad

Bunu, belirli kare renklerde bazı parçaların asla bulunmayacağını hesaba katarak geliştirebilir misiniz?
Andrew Rollings

andrew: aslında yapamam. Bir kraliçeye yükseltilmiş bir piyonu hesaba katmayı unuttum (cadrian'ın cevabının önerdiği gibi). Görünüşe göre gerçekten fazladan bir
parçaya

Siyah beyaz fillerin birlikte nasıl tanımlanabileceğini görebiliyorum. Şövalyeleri merak ediyorum ..
int3

1
Kraliçe olmayan terfileri kaçırıyorsun.
Loren Pechtel

2

Çalışma uzunluğu kodlaması kullanırım. Bazı parçalar benzersizdir (veya yalnızca iki kez bulunur), bu nedenle onlardan sonraki uzunluğu atlayabilirim. Cletus gibi, 13 benzersiz duruma ihtiyacım var, böylece parçayı kodlamak için bir yarım bayt (4 bit) kullanabilirim. İlk tahta daha sonra şöyle görünecektir:

White Rook, W. Knight, W. Bishop, W. Queen, W. King, W. Bishop, W. Knight, W. Rook,
W. Pawn, 8,
Empty, 16, Empty, 16
B. Pawn, 8,
B. Rook, B. Knight, B. Bishop, B. Queen, B. King, B. Bishop, B. Knight, B. Rook

bu da bana 8 + 2 + 4 + 2 + 8 yarım bayt = 24 yarım = 96 bit bırakıyor. 16'yı yarım bayt ile kodlayamıyorum ama "Boş, 0" mantıklı olmadığından "0" ı "16" olarak kabul edebilirim.

Tahta boşsa ancak sol üst köşedeki tek bir piyon için, "Piyon, 1, Boş, 16, Boş, 16, Boş 16, Boş, 15" = 10 bit = 40 bit elde ederim.

En kötü durum, her parçanın arasında boş bir kare olması. Ama parçanın kodlanması için 16 değerden 13'üne ihtiyacım var, yani belki başka bir tanesini "Boş1" demek için kullanabilirim. O halde 64 nibbles == 128bit'e ihtiyacım var.

Hareketler için, parça için 3 bit (renk her zaman önce beyazın hareket ettiği gerçeğiyle verilir) artı yeni konum için 5 bit (0..63) = hareket başına bir bayt gerekir. Çoğu zaman, menzil içinde yalnızca tek bir parça olacağı için eski pozisyona ihtiyacım yok. Garip durum için, tek kullanılmayan kodu (parçayı kodlamak için sadece 7 koda ihtiyacım var) ve ardından eski için 5 bit ve yeni konum için 5 bit kullanmalıyım.

Bu, rok atmayı 13 ısırıkta kodlamama izin veriyor (Kralı Kaleye doğru hareket ettirebilirim ki bu da ne istediğimi söylemek için yeterlidir).

[DÜZENLE] Akıllı bir kodlayıcıya izin verirseniz, ilk kurulum için 0 bit'e ihtiyacım var (çünkü herhangi bir şekilde kodlanması gerekmiyor: Statiktir) artı hareket başına bir bayta.

[DÜZENLEME2] Bu piyon dönüşümünü terk eder. Bir piyon son sıraya ulaşırsa, "dönüşümler" demek için onu yerine taşıyabilirim ve ardından değiştirildiği taş için 3 bit ekleyebilirim (bir vezir kullanmak zorunda değilsiniz; piyonu herhangi bir şeyle değiştirebilirsiniz ama Kral).


Akıllı kodlayıcı, bunun bütün bir oyun olduğunu varsayamaz. Bir oyunun parçası olabilir. Sanırım yine de başlangıç ​​pozisyonlarını kodlaman gerekecek.
Andrew Rollings

En kötü durumda, ya 128 bite ihtiyacım var ya da oyun hala erken aşamadaysa, onu başlangıç ​​konumuna getirmek için 15 hamle = 120 bit kullanabilirim.
Aaron Digulla

Sadece başlangıç ​​panosu durumu değil, HERHANGİ bir durum kodlanacağından, parçaların kendisini de kodlamanız gerekir. Bu yüzden parça başına en az 5 bite ihtiyacınız olacak. Bu size en az 32 * 5 bit fazladan verecek
Toad

@reiner: Yanılıyorsun. Parça / boş kare başına dört bit'e ihtiyacım var. Ve bunu cevabımın ilk kısmında zaten ele aldım, bu yüzden "32 * 5 bit ekstra" yok. Başlangıç ​​durumu için 96 bite ihtiyacım var ve diğer herhangi bir başlangıç ​​durumu için en fazla 128 bite ihtiyacım var.
Aaron Digulla

Harun: Yine de söylediğiniz gibi, en kötü durum senaryosu bu kodlamadaki gerçekten en kötü durumdur. Bir başlangıç ​​tahtasından 3 veya 4 hareketten sonra, kodlamanız daha fazla boş bit ekledikçe önemli ölçüde daha fazla bit gerektirecektir
Toad

2

Oyunları kitaplara ve kağıtlara kodladıkları gibi: her parçanın bir sembolü vardır; "yasal" bir oyun olduğu için, önce beyaz hareketler - beyaz veya siyahı ayrı ayrı kodlamaya gerek yok, kimin hareket ettiğini belirlemek için sadece hamle sayısını sayın. Ayrıca, her hareket (parça, bitiş konumu) olarak kodlanır ve burada 'bitiş konumu' belirsizlikleri ayırt etmeye izin veren en az sembol miktarına indirgenir (sıfır olabilir). Oyunun uzunluğu hamle sayısını belirler. Ayrıca her adımda (son hareketten itibaren) zamanı dakika olarak kodlayabilirsiniz.

Parçanın kodlanması, her birine (toplam 32) bir sembol atayarak veya sınıfa bir sembol atayarak ve hangi parçanın hareket ettiğini anlamak için bitiş konumunu kullanarak yapılabilir. Örneğin, bir piyonun 6 olası bitiş konumu vardır; ancak ortalama olarak her fırsatta sadece bir çift müsaittir. Bu nedenle, istatistiksel olarak, konumu sonlandırarak kodlama bu senaryo için en iyisi olabilir.

Hesaplamalı sinirbilimde (AER) ani trenler için benzer kodlamalar kullanılır.

Dezavantajlar: Bağlantılı bir listeyi gezinmeye benzer şekilde, mevcut duruma geçmek ve bir alt küme oluşturmak için tüm oyunu yeniden oynamanız gerekir.


Sadece bir oyun parçası olabilir. Beyaz hamlelerin ilk olduğunu varsayamazsınız.
Andrew Rollings

2

64 olası kart konumu vardır, bu nedenle her konum için 6 bit gerekir. 32 başlangıç ​​parçası var, bu nedenle şimdiye kadar toplam 192 bitimiz var, burada her 6 bit verilen parçanın konumunu gösteriyor. Parçaların görüneceği sırayı önceden belirleyebiliriz, böylece hangisinin hangisi olduğunu söylememize gerek kalmaz.

Ya tahtadan bir parça çıkarsa? Tahtanın dışında olduğunu belirtmek için başka bir taşla aynı noktaya bir taş yerleştirebiliriz, aksi takdirde bu yasa dışı olur. Ancak ilk parçanın tahtada olup olmayacağını da bilmiyoruz. Bu yüzden hangi parçanın ilk olduğunu gösteren 5 bit ekliyoruz (32 olasılık = ilk parçayı temsil etmek için 5 bit). Daha sonra o noktayı tahta dışındaki sonraki parçalar için kullanabiliriz. Bu bizi toplamda 197 bit'e getiriyor. Tahtada işe yarayacak en az bir parça bulunmalıdır.

Öyleyse sıranın geldiği bir bit'e ihtiyacımız var - bizi 198 bit'e getiriyor .

Peki ya rehin terfisi? 42 bit ekleyerek piyon başına 3 bit ekleyerek kötü bir şekilde yapabiliriz. Ama sonra çoğu zaman piyonların terfi etmediğini fark edebiliriz.

Yani tahtadaki her piyon için '0' biti terfi etmediğini gösterir. Tahtada bir piyon yoksa, o zaman hiç ihtiyacımız yoktur. Daha sonra terfi için sahip olduğu değişken uzunluklu bit dizgilerini kullanabiliriz. Çoğunlukla bir vezir olacağı için "10", KRALİÇE anlamına gelebilir. Sonra "110" kale, "1110" fil ve "1111" at anlamına gelir.

İlk durum 198 + 16 = 214 bit alacaktır , çünkü 16 piyonun tamamı tahtadadır ve terfi ettirilmemiştir. İki terfi piyon-vezire sahip bir oyun sonu, 198 + 4 + 4 gibi bir şey alabilir, yani 4 piyon canlı ve terfi etmemiş ve 2 vezir piyon, toplam 206 bit için . Oldukça sağlam görünüyor!

===

Huffman kodlaması, diğerlerinin de belirttiği gibi, bir sonraki adım olacak. Birkaç milyon oyun gözlemlerseniz, her bir parçanın belirli karelerde olma olasılığının çok daha yüksek olduğunu fark edeceksiniz. Örneğin, çoğu zaman piyonlar düz bir çizgide veya biri sola / biri sağa kalır. Kral genellikle ana üssün etrafında takılır.

Bu nedenle, her ayrı konum için bir Huffman kodlama şeması tasarlayın. Piyonlar muhtemelen 6 yerine ortalama 3-4 bit alacaktır. Şah da birkaç bit almalıdır.

Ayrıca bu şemaya, olası bir pozisyon olarak "alınan" ifadesini dahil edin. Bu, rok atmayı da çok sağlam bir şekilde idare edebilir - her kale ve şah ekstra bir "orijinal pozisyon, taşınmış" durumuna sahip olacaktır. Ayrıca piyonlarda geçen geçmeyi de bu şekilde kodlayabilirsiniz - "orijinal konum, geçerken geçebilir".

Yeterli veriyle bu yaklaşım gerçekten iyi sonuçlar vermelidir.


2
Sadece çıkarılan taşları kral ile aynı kareye atayın. Kral asla ortadan kaldırılamayacağı için belirsiz olmaz
John La Rooy

Bu iyi bir yorum :) Bu çözümün de güzel yönleri. Bir kazanan seçmenin bu kadar zor olacağını bilmiyordum.
Andrew Rollings

2

Huffman kodlamasını kullanmayı denerdim . Bunun arkasındaki teori şudur - her satranç oyununda çok hareket eden bazı taşlar olacaktır ve bazıları çok hareket etmeyen veya erken elenmeyecektir. Başlangıç ​​pozisyonunda bazı parçalar zaten çıkarılmışsa - çok daha iyi. Aynısı kareler için de geçerlidir - bazı kareler tüm eylemi görebilirken, bazılarına fazla dokunulmaz.

Böylece iki Huffman masam olacak - biri parçalar için, diğeri kareler için. Gerçek oyuna bakılarak oluşturulacaklar. Her kare kare çifti için büyük bir masam olabilirdi, ancak bence bu oldukça verimsiz olurdu çünkü aynı karede aynı parçanın birçok örneği tekrar hareket etmiyor.

Her parçanın atanmış bir kimliği olacaktır. 32 farklı parça olduğundan, parça kimliği için sadece 5 bite ihtiyacım olacak. Parça kimlikleri oyundan oyuna değişmiyor. Aynı şey 6 bite ihtiyacım olan kare kimlikleri için de geçerli.

Huffman ağaçları, her bir düğüm sıralı olarak geçerken aşağıya yazılarak kodlanacaktır (yani, önce düğüm çıktı, sonra onun çocukları soldan sağa). Her düğüm için bir yaprak düğüm mü yoksa bir dal düğümü mü olduğunu belirten bir bit olacaktır. Bir yaprak düğüm ise, kimliği veren bitler takip edecektir.

Başlangıç ​​pozisyonu bir dizi parça konum çifti tarafından verilecektir. Bundan sonra her hareket için bir parça konum çifti olacaktır. Başlangıç ​​pozisyonu tanımlayıcısının sonunu (ve hareket tanımlayıcısının başlangıcını) sadece iki kez bahsedilen ilk parçayı bularak bulabilirsiniz. Bir piyon terfi ettiğinde, ne olacağını belirten fazladan 2 bit olacaktır, ancak taş kimliği değişmeyecektir.

Oyunun başlangıcında bir piyonun terfi etme olasılığını hesaba katmak için, huffman ağaçları ile veriler arasında bir "terfi masası" da olacaktır. İlk başta, kaç piyonun yükseltileceğini belirten 4 bit olacaktır. Daha sonra her bir piyon için huffman kodlu kimliği ve ne hale geldiğini belirten 2 bit olacaktır.

Huffman ağaçları, tüm veriler (hem başlangıç ​​pozisyonu hem de hareketler) ve terfi tablosu dikkate alınarak oluşturulacaktır. Normalde terfi tablosu boş olacak veya sadece birkaç girdiye sahip olacaktır.

Grafiksel terimlerle özetlemek gerekirse:

<Game> := <Pieces huffman tree> <squares huffman tree> <promotion table> <initial position> (<moves> | <1 bit for next move - see Added 2 below>)

<Pieces huffman tree> := <pieces entry 1> <pieces entry 2> ... <pieces entry N>
<pieces entry> := "0" | "1" <5 bits with piece ID>

<squares huffman tree> := <squares entry 1> <squares entry 2> ... <squares entry N>
<Squares entry> := "0" | "1" <6 bits with square ID>

<promotion table> := <4 bits with count of promotions> <promotion 1> <promotion 2> ... <promotion N>
<promotion> := <huffman-encoded piece ID> <2 bits with what it becomes>

<initial position> := <position entry 1> <position entry 2> ... <position entry N>
<moves> := <position entry 1> <position entry 2> ... <position entry N>
<position entry> := <huffman-encoded piece ID> <huffman-encoded squre ID> (<2 bits specifying the upgrade - optional>)

Eklendi: Bu yine de optimize edilebilir. Her parçanın sadece birkaç yasal hamlesi vardır. Hedef kareyi basitçe kodlamak yerine, her parçanın olası hareketleri için 0 tabanlı kimlik verilebilir. Her taş için aynı kimlikler yeniden kullanılacak, bu nedenle toplamda 21'den fazla farklı kimlik olmayacak (vezir en fazla 21 farklı olası hareket seçeneğine sahip olabilir). Bunu alanlar yerine huffman masasına koyun.

Ancak bu, orijinal durumu temsil etmede bir zorluk yaratacaktır. Her bir parçayı yerine koymak için bir dizi hareket üretilebilir. Bu durumda, başlangıç ​​durumunun sonunu ve hareketlerin başlangıcını bir şekilde işaretlemek gerekli olacaktır.

Alternatif olarak, sıkıştırılmamış 6 bitlik kare kimlikleri kullanılarak yerleştirilebilirler.

Bunun boyutta genel bir azalma sağlayıp sağlamayacağını - bilmiyorum. Muhtemelen, ama biraz denemeli.

2 eklendi: Bir özel durum daha. Oyun durumunda HİÇ hamle yoksa, sonraki hamleyi kimin yapacağını ayırt etmek önemlidir. Bunun için sonuna bir parça daha ekleyin. :)


2

[soruyu doğru bir şekilde okuduktan sonra düzenlenir] Her yasal konuma ilk konumdan ulaşılabileceğini varsayarsanız (bu, "yasal" ifadesinin olası bir tanımıdır), o zaman herhangi bir konum, başlangıçtan itibaren hamle sırası olarak ifade edilebilir. Standart olmayan bir konumdan başlayan bir oyun parçası, başlangıca ulaşmak için gereken hareketlerin sırası, kamerayı açmak için bir anahtar ve ardından sonraki hamleler olarak ifade edilebilir.

Öyleyse ilk kart durumuna tek bit "0" diyelim.

Herhangi bir konumdan hareketler, kareleri numaralandırarak ve hamleleri (başlangıç, bitiş) sıralayarak numaralandırılabilir, geleneksel 2 kare sıçrama rok atmayı gösterir. Kurallara aykırı hareketleri kodlamaya gerek yoktur, çünkü tahta konumu ve kurallar her zaman zaten bilinmektedir. Kamerayı açacak bayrak, özel bir bant içi hareket olarak veya daha mantıklı bir şekilde bant dışı bir hareket sayısı olarak ifade edilebilir.

Her iki taraf için her biri 5 bit sığabilen 24 açılış hareketi vardır. Sonraki hareketler daha fazla veya daha az bit gerektirebilir, ancak yasal hareketler her zaman numaralandırılabilir, bu nedenle her hareketin genişliği mutlu bir şekilde büyüyebilir veya genişleyebilir. Hesaplamadım, ancak 7 bitlik konumların nadir olacağını düşünüyorum.

Bu sistemi kullanarak 100 yarım hamle oyunu yaklaşık 500 bit olarak kodlanabilir. Ancak, bir açılış kitabı kullanmak akıllıca olabilir. Bir milyon dizi içerdiğini varsayalım. O halde, bir başlangıç ​​0 standart panodan bir başlangıcı gösterir ve 1 ve ardından gelen 20 bitlik bir sayı, bu açılış dizisinden bir başlangıcı gösterir. Biraz geleneksel açılışlara sahip oyunlar, örneğin 20 yarım hareket veya 100 bit ile kısaltılabilir.

Bu, mümkün olan en büyük sıkıştırma değildir, ancak (açılış kitabı olmadan) sorunun varsaydığı gibi halihazırda bir satranç modeliniz varsa uygulanması çok kolay olacaktır.

Daha fazla sıkıştırmak için, hamleleri keyfi bir sırayla değil, olasılığa göre sıralamak ve olası dizileri daha az bitte kodlamak (örneğin, insanların bahsettiği gibi Huffman jetonlarını kullanarak) istersiniz.


Başlangıç ​​konumu mutlaka bilinmemektedir. Bir oyun parçası olabilir.
Andrew Rollings

@Andrew: evet. benim hatam. Oyun parçalarına izin vermek için düzenledim.
Douglas Bagnall

2

Hesaplama süresi bir sorun değilse, belirli bir konuma benzersiz kimlikler atamak için deterministik bir olası konum üreteci kullanabilirsiniz.

Belirli bir konumdan ilk önce deterministik bir malikanede olası konumların sayısını üretin, örneğin sol alttan başlayarak sağ üste doğru hareket edin. Bu, bir sonraki hareket için kaç bit'e ihtiyacınız olacağını belirler, bazı durumlarda bir kadar küçük olabilir. Sonra taşıma yapıldığında, o hareket için sadece benzersiz kimliği saklayın.

Terfi ve diğer kurallar, belirleyici bir şekilde ele alındıkları sürece, örneğin vezire, kaleye, fil için ayrı bir hareket olarak sayıldıkları sürece geçerli hamleler olarak sayılır.

Başlangıç ​​pozisyonu en zor olanıdır ve yaklaşık 250 milyon olası pozisyon oluşturabilir (sanırım) ki bu da yaklaşık 28 bit artı kimin hareketini belirlemek için fazladan bir bit gerektirir.

Sıranın kimde olduğunu bildiğimizi varsayarsak (her dönüş beyazdan siyaha döner) deterministik jeneratör şöyle görünür:

for each row
    for each column
        add to list ( get list of possible moves( current piece, players turn) )

"olası hareketlerin listesini al" şunun gibi bir şey yapacaktır:

if current piece is not null 
    if current piece color is the same as the players turn
        switch( current piece type )
            king - return list of possible king moves( current piece )
            queen - return list of possible queen moves( current piece )
            rook - return list of possible rook moves( current piece )
            etc.

Şah kontrol altındaysa, her 'olası xxx hamleleri listesi' yalnızca kontrol durumunu değiştiren geçerli hamleler döndürür.


Bu sinsi bir çözüm ... yani ... bu durumda, deterministik sayıyı üretmek için algoritmanızı tanımlayın.
Andrew Rollings

Bir satranç tahtasında her pozisyonu oluşturmanın ne kadar süreceği konusunda ilginç bir bağlantı buldum
Andrew Rollings

2

Cevapların çoğu 3 kat tekrarı gözden kaçırdı. maalesef 3 kat tekrar için şu ana kadar oynanan tüm pozisyonları kaydetmeniz gerekiyor ...

Soru, alanı verimli bir şekilde depolamamızı gerektirdi, bu nedenle, onu hareketler listesinden oluşturabildiğimiz sürece (standart başlangıç ​​konumumuz olması koşuluyla) konumu depolamaya gerçekten ihtiyacımız yok. PGN'yi optimize edebiliriz ve işte bu kadar. Körük basit bir şemadır.

Tahtada 64 kare vardır, 64 = 2 ^ 6. Her hareketin sadece ilk ve son karesini saklarsak 12 bit alır (Promosyon daha sonra ele alınacaktır). Bu şemanın halihazırda oyuncuların hareket etmesini, baskılayıcı, ele geçirilen taş, rok atma vb. bunlar sadece hareket listesinin tekrar oynatılmasıyla oluşturulabilir.

terfi için "N hamlesinde XYZ Parçasına yüksel" diyebilecek ayrı bir vektör dizisi tutabiliriz. (int, bayt) 'ın bir vektörünü tutabiliriz.

(Nereden, Nereden) vektörünü optimize etmek de cazip geliyor, çünkü bu (Nereden, Nereden) vektörlerinin çoğu satrançta mümkün değil. Örneğin. e1'den d8'e vb. bir hareket olmayacak. Ama herhangi bir şema bulamadım. Başka fikirler en çok memnuniyetle karşılanır.


2

Bunu uzun süredir düşündüm (+ - 2 saat). Ve apaçık bir cevap yok.

Varsayalım:

  1. Zaman durumunu görmezden gelmek (Bir oyuncu bir zaman sınırına sahip değildi, bu nedenle oynamayarak bir beraberliği zorlayabilirdi)
  2. Oyun ne zaman oynandı?!? Bu önemlidir çünkü kurallar zamanla değişmiştir (sonraki noktada modern bir oyun varsayılacaktır ...) Lütfen örneğin ölü piyon kuralına bakın (wikipedia'da bunu gösteren çok meşhur bir problem var) ve isterseniz zamanda geriye gitmek için iyi şans fili sadece yavaş hareket ederdi ve zar kullanılırdı. lol.

... bu yüzden güncel modern kurallar. İlk olarak, tekrarlama ve hareket tekrarlama limitine bakılmaksızın.

-C 25 bayt yuvarlanmış (64b + 32 * 4b + 5b = 325b)

= 64 bit (bir şey / hiçbir şey) + 32 * 4 bit [1bit = renkli {siyah / soluk} + 3bit = taş türü {Şah, Kız, Piskopos, kNight, Kale, Piyon, Taşınan Piyon} NB: Taşınmış piyon ... örneğin, bir 'geçerken alma'nın mümkün olduğunu gösteren bir önceki sıradaki son hareket eden piyonsa. ] Gerçek durum için + 5bit (kim sırayla, geçerken, her iki tarafta rok yapıp yapmama olasılığı)

Çok uzak çok iyi. Muhtemelen geliştirilebilir, ancak daha sonra dikkate alınması gereken değişken uzunluk ve promosyon olacaktır !?

Şimdi, aşağıdaki kurallar sadece bir oyuncu bir beraberlik için başvurduğunda geçerlidir, BU otomatik DEĞİLDİR! Öyleyse, bu 90 hamleyi ele geçirmeden veya bir piyon hareketinin, eğer hiçbir oyuncu beraberlik istemezse mümkün olduğunu düşünün! Yani, tüm hareketlerin kaydedilmesi ve mevcut olması gerekir.

-D pozisyonun tekrarı ... örn. Yukarıda belirtildiği gibi tahta durumu (bkz. C) veya değil ... (FIDE kuralları ile ilgili olarak aşağıya bakınız) -E 50 hamle ödeneği gibi karmaşık problemi ele geçirme veya piyon hareketi olmadan bırakır. sayaç gerekli ... Ancak.

Peki bununla nasıl başa çıkıyorsunuz? ... Gerçekten hiçbir yolu yok. Çünkü hiçbir oyuncu berabere kalmak istemeyebilir veya bunun gerçekleştiğini fark edemez. Şimdi, E durumunda bir sayaç yeterli olabilir ... ama işte hile ve hatta FIDE kurallarını okumak (http://www.fide.com/component/handbook/?id=124&view=article) cevap ... ya roket becerisinin kaybı. Bu bir tekrar mı? Sanmıyorum ama o zaman bu konuya değinilmemiş, netleştirilmemiş bulanık bir konu.

İşte kodlamaya çalışmak için bile karmaşık veya tanımlanmamış iki kural var ... Şerefe.

Bu yüzden, bir oyunu gerçekten kodlamanın tek yolu, hepsini en baştan kaydetmektir ... bu daha sonra "tahta durumu" sorusuyla çelişir (veya değil?).

Umarım bu yardımcı olur ... çok fazla matematik değil :-) Sadece bazı soruların o kadar kolay olmadığını, yoruma veya ön bilginin doğru ve verimli olabilmesi için fazla açık olmadığını göstermek için. Çok fazla solucan kutusu açtığı için röportaj yapmayı düşünmediğim bir şey değil.


2

Yacoby'nin çözümünde başlangıç ​​pozisyonunda olası iyileştirme

Hiçbir yasal pozisyonda her renkten 16'dan fazla parça yoktur. 64 kare üzerine 16 siyah ve 16 beyaz parçayı yerleştirmenin yol sayısı yaklaşık 3.63e27'dir. Log2 (3.63e27) = 91.55. Bu, tüm parçaların konumunu ve rengini 92 bitte kodlayabileceğiniz anlamına gelir. Bu, konum için 64 bitten azdır + Yacoby'nin çözümünün gerektirdiği renk için 32 bite kadar. En kötü durumda, kodlamada önemli ölçüde karmaşıklık pahasına 4 bit kaydedebilirsiniz.

Öte yandan, 5 veya daha fazla parçanın eksik olduğu pozisyonlar için boyutu artırır. Bu pozisyonlar, tüm pozisyonların yalnızca <% 4'ünü temsil eder, ancak muhtemelen başlangıç ​​pozisyonundan farklı bir başlangıç ​​pozisyonu kaydetmek istediğiniz durumların çoğunluğunu oluştururlar.

Bu tam çözüme götürür

  1. Yukarıdaki yönteme göre parçaların konumunu ve rengini kodlayın. 92 bit .
  2. Her bir taşın türünü belirtmek için bir Huffman Kodu kullanın: piyon: '0', kale: '100', at: '101', fil: '110', kız: '1110', şah: '1111'. Bu, tam bir parça seti için (16 * 1 + 12 * 3 + 4 * 4) = 68 bit gerektirir . Tam kart konumu maksimum 92 + 68 = 160 bit olarak kodlanabilir .
  3. Ek oyun durumu eklenmelidir: turn: 1 bit, hangi rok atmanın mümkün olduğu: 4 bit, "geçerken" mümkün: 4 bit'e kadar (1 bit durumun böyle olduğunu ve 3 bit hangisinin olduğunu söyler). Başlangıç ​​pozisyonu = 160 + 9 = 169 bit olarak kodlanmıştır
  4. Hareket listesi için, belirli bir konum için olası tüm hareketleri numaralandırın ve listedeki hareketin konumunu saklayın. Hamle listesi tüm özel durumları içerir (rok atma, geçerken alma ve istifa etme). En yüksek konumu saklamak için yalnızca gerektiği kadar bit kullanın. Ortalama olarak, hareket başına 7 biti geçmemelidir (ortalama olarak parça başına 16 olası parça ve 8 yasal hareket). Bazı durumlarda, bir hareket zorlandığında, sadece 1 bit gerektirir (hareket ettirin veya vazgeçin).

1

Tahtada 32 parça var. Her parçanın bir konumu vardır (64 karede bir). Yani sadece 32 pozitif tamsayıya ihtiyacınız var.

64 pozisyonun 6 bitte tutulduğunu biliyorum ama bunu yapmam. Son parçaları birkaç bayrak için saklardım (düşen taş, kraliçenin piyonu)


Durumu tutmak için bayrak kullanmanıza gerek yoktur. Kodlayıcınızın "kuralları bilecek" kadar akıllı olduğunu varsayabilirsiniz. Bu nedenle, eğer bir piyon aniden vezire değiştiyse, bunun kodlamada özel olarak işaretlenmesi gerekmez (sanırım, oyuncu terfi etmemeyi seçmediği sürece).
Andrew Rollings

evet olmalı, çünkü bir piyonun ilk pozisyonundan piyonun terfi edip etmediğini söyleyemezsiniz! Böylece ilk kurulumda kodlanacak
Toad

Ah, ama zaten terfi edilip edilmediğini neden bilmen gerekiyor? Bu sadece bir parça. Geçmiş durumu, bu durumda önemsiz olacaktır.
Andrew Rollings

Bence bir piyon hala bir piyonsa veya bir vezire terfi ettirilmişse, oyunun geri kalanı için pek önemsiz değil. Öyle düşünmüyorsanız, sizinle satranç oynamayı çok isterim; ^)
Toad

@reinier: Mevcut vezirin aslen kraliçe mi yoksa aslen piyon mu olmasının alakasız olduğunu iddia ediyor .
A. Rex

1

cletus'un cevabı güzel, ama sıranın kimin olduğunu da kodlamayı unuttu. Mevcut durumun bir parçasıdır ve bu durumu bir arama algoritması (alfa-beta türevi gibi) çalıştırmak için kullanıyorsanız gereklidir.

Ben bir satranç oyuncusu değilim, ancak bir köşe durumu daha olduğuna inanıyorum: kaç hamle tekrarlandı. Her oyuncu üç kez aynı hamleyi yaptığında, oyun berabere biter, değil mi? Öyleyse, bu bilgileri duruma kaydetmeniz gerekir çünkü üçüncü tekrardan sonra durum artık terminaldir.


Bu rotaya giderken, her iki oyuncu için de oynanan süreyi eklemeniz gerekir çünkü gerçek bir satranç oyununda her iki oyuncu da toplamda sadece 1 veya 2 saat düşünür.
Toad

2
Gerçek verilerdeki kuralları kodlamanız gerekmez. Kodlayıcının kendisinin gerekli olan kuralları bildiğini varsayabilirsiniz.
Andrew Rollings

Ah .. Zaman oynamayı düşünmedim. İyi çağrı ... :)
Andrew Rollings

@Andrew Rollings: Kural, durum tabanlıdır, olduğu gibi, yalnızca belirli bir ön koşul karşılandığında tetiklenir. Ön koşulun bu durumunu izlemek de ... peki, durumunun bir parçasıdır. :)
Shaggy Frog

Bu durumda alakasız. Gerekirse, kod çözücü kazananı belirlemek için durumu inceleyebilir. Unutmayın, kodlayıcı / kod çözücü kural farkındadır. Gerçekten kodlanması gereken şeyler oyuncunun seçimleridir - diğer her şeyin kodlayıcı / kod çözücü tarafından bilindiği varsayılabilir.
Andrew Rollings

1

Diğerlerinin de bahsettiği gibi, 32 parçanın her biri için hangi karede olduklarını saklayabilirsiniz ve eğer tahtadalarsa ya da değillerse bu 32 * (log2 (64) + 1) = 224 bit verir.

Bununla birlikte, filler yalnızca siyah veya beyaz kareleri işgal edebilir, bu nedenle bunlar için konum için yalnızca 28 * 7 + 4 * 6 = 220 bit veren log2 (32) bitine ihtiyacınız vardır.

Ve piyonlar arkadan başlamadığından ve yalnızca ileriye doğru hareket edebildiğinden, yalnızca 56'da olabilirler, bu sınırlamayı piyonlar için gereken bit sayısını azaltmak için kullanmak mümkün olmalıdır.


filler de oyun tahtasının dışında olabilir, bu yüzden bunlar için fazladan bir parçaya ihtiyacınız var. Ayrıca terfi edilmiş piyonları ve ilk başlayacak kişiyi unutuyorsunuz. Bütün bunları hesaba katarsak, temelde cevabımı elde edersiniz; ^)
bulursunuz Toad

Fillerin 6 biti log2 (32) + 1 = 6'dır, ancak tüm ayrıntıları göz önünde bulundurduğunuzda bu kesinlikle karmaşık bir sorudur :)
Andreas Brinck

Bu satırlarda düşünüyordum ama yardımcı olmadı. Thomas'ın cevabına bakın ve boş alanlar kavramını ortadan kaldırmak için huffman kodlamasını değiştirin. Kareleri dolu olan matrisi depolamak için 64 bit kullanırsınız ve her kodlamadan 1 bit kaldırırsınız - böylece aynı 64 biti tam olarak geri kazanırsınız.
Loren Pechtel

1

Bir kartın 64 karesi vardır ve bir karenin boş olup olmadığını gösteren 64 bit ile temsil edilebilir. Sadece bir karede bir parça varsa parça bilgisine ihtiyacımız var. Oyuncu + parça 4 bit aldığından (daha önce gösterildiği gibi) mevcut durumu 64 + 4 * 32 = 192 bit olarak alabiliriz. Mevcut turda atın ve 193 bitiniz olur.

Bununla birlikte, her parça için yasal hareketleri de kodlamamız gerekiyor. İlk olarak, her bir parça için geçerli hamle sayısını hesaplıyoruz ve tam bir karenin parça tanımlayıcısından sonra o kadar biti ekliyoruz. Şu şekilde hesapladım:

Piyon: İleri, önce iki ileri, geçerken * 2, terfi = 7 bit. İlk dönüşü ve yükselmeyi aynı konumdan gerçekleşemeyeceği için tek bir bitte birleştirebilirsiniz, böylece 6'ya sahip olursunuz. Kale: 7 dikey kare, 7 yatay kare = 14 bit At: 8 kare = 8 bit Piskopos: 2 köşegen * 7 = 14 bit Kraliçe: 7 dikey, 7 yatay, 7 çapraz, 7 çapraz = 28 bit Kral: 8 çevreleyen kare

Bu yine de hedeflenen kareleri mevcut konuma göre eşlemeniz gerektiği anlamına gelir, ancak bu basit bir hesaplama (olmalıdır).

16 piyonumuz, 4 kale / at / fil ve 2 vezir / papazımız olduğundan, bu 16 * 6 + 4 * 14 + 4 * 8 + 4 * 14 + 2 * 28 + 2 * 8 = 312 bit daha toplam 505 bittir.

Muhtemel hareketler için parça başına gereken bit sayısına gelince, buna bazı optimizasyonlar eklenebilir ve bit sayısı muhtemelen azaltılabilir, sadece çalışmak için kolay sayılar kullandım. Örneğin, kayan parçalar için ne kadar uzağa hareket edebileceklerini saklayabilirsiniz, ancak bu ekstra hesaplamalar gerektirir.

Uzun lafın kısası: Sadece bir kare dolu olduğunda fazladan verileri (parça, vb.) Depolayın ve her bir parçanın yasal hareketlerini temsil etmesi için yalnızca minimum sayıda biti saklayın.

DÜZENLEME1: Herhangi bir parçaya rok atma ve piyon terfisini unuttum. Bu, açık pozisyonlarla toplamı 557 hamleye çıkarabilir (piyonlar için 3 bit daha, şahlar için 2 bit)


1

Her bir parça 4 bit (piyondan şaha, 6 tür), siyah / beyaz = 12 değerle temsil edilebilir

Tahtadaki her kare 6 bit ile temsil edilebilir (x kordonu, y kordonu).

İlk pozisyonlar maksimum 320 bit (32 parça, 4 + 6 bit) gerektirir

Sonraki her hareket 16 bit ile temsil edilebilir (konumdan konuma, parça).

Castling, ikili bir hareket olduğu için ekstra 16 bit gerektirir.

Vezir piyonlar, 4 bitten 4 yedek değerden biri ile temsil edilebilir.

Ayrıntılı olarak matematiği yapmadan, bu, 32 * 7 bit (önceden tanımlanmış parça dizisi) veya 64 * 4 bit (önceden tanımlanmış kareler ataması) depolamaya kıyasla ilk hareketten sonra yerden tasarruf etmeye başlar.

Her iki tarafta 10 hareketten sonra, gereken maksimum alan 640 bittir

... ama sonra tekrar, her bir parçayı benzersiz olarak (5 bit) tanımlar ve vezir piyonları işaretlemek için altıncı bit eklersek, o zaman her hareket için yalnızca taş kimliği + pozisyona ihtiyacımız var. Bu, hesaplamayı şu şekilde değiştirir ...

Başlangıç ​​konumları = maksimum 384 bit (32 parça, 6 + 6 bit) Her hareket = 12 bit (konuma, parça kimliği)

Sonra her iki tarafta 10 hareketten sonra gereken maksimum alan 624 bittir


İkinci seçenek, depolamanın her kayıt = konum ve parça olmak üzere 12 bitlik kayıtlar olarak okunabilmesi gibi ek bir avantaja sahiptir. İlk hareket kabini, parçanın önceden bir girişe sahip olmasıyla algılanır.
Steve De Caux

hareketler arasındaki süre için her kayda sayaç için x bit ekleyin. Kurulum kayıtları için bu 0 olarak ayarlanacaktır.
Steve De Caux

Yazacağım yaklaşım buydu. Bir optimizasyon, standart oyunlar için, başlangıç ​​konumlarını hiç kodlamanıza gerek olmamasıdır - kafada "bu standart bir oyundur" diyen tek bir bit yeterlidir. Ayrıca, rok atma iki hamle gerektirmez - çünkü bir rok hareketi her zaman açıktır ve belirli bir kral yarı yarıya düştüğünde kalenin hareket etmesi için tek bir geçerli yol vardır, fazlalık bilgidir. Terfi için, bir piyon son sıraya hareket ettikten sonra yeni taş tipini belirlemek için 4 biti kullanabilirsiniz.
kyoryu

Yani, tipik bir oyun için, 10 hamleden sonra, herhangi bir terfi olmadığı varsayılarak 121 bitte olursunuz. Atipik oyunlar bayrak için 1 bit, taş * 10 bit ve ilk oyuncuyu belirtmek için başka bir bit gerektirir. Ayrıca, belirli bir karedeki taş oyundaki önceki hamlelerden örtük olduğundan, her hareket yalnızca 12 bit gerektirir. Bu muhtemelen önerilen yöntemlerin bazılarından daha az etkilidir, ancak oldukça temiz ve "standart" oyunlar için makul ölçüde etkilidir.
kyoryu

@kyoro - Standart kurulum için bir boş fikrinizi kullanarak hamle başına 12 bitin dövülebileceğine ikna olmadım (yine de sıfır için ayarlanmış 12 bit kullanırdım) - her iki tarafta 1000 hareketten sonra bu 24012 bit aka 3002 bayt (yuvarlanmış) Bir tür sıkıştırma kullansanız bile, sözlüğünüzü sabit kodlu (veya mantıksal olarak türetilebilir, aynı şeyi) ilan ederek bunu yenmek için hile yapmanız gerekir
Steve De Caux

1

Robert G gibi, standart olduğu ve çok çeşitli araçlar tarafından kullanılabileceği için PGN kullanma eğilimindeydim.

Bununla birlikte, uzaktaki bir uzay aracında bulunan bir satranç yapay zekası oynuyorsam ve bu nedenle her parçası değerliyse, hamleler için bunu yapardım. İlk durumu kodlamaya daha sonra geleceğim.

Hareketlerin durumu kaydetmesine gerek yok; kod çözücü, herhangi bir noktada hangi hareketlerin yasal olduğunun yanı sıra durumu da takip edebilir. Kaydedilmesi gereken tüm hamleler, çeşitli yasal alternatiflerden hangisinin seçildiğidir. Oyuncular değiştiği için, bir hareketin oyuncu rengini kaydetmesine gerek yoktur. Bir oyuncu sadece kendi renkli taşlarını hareket ettirebildiğinden, ilk seçim oyuncunun hangi taşı hareket ettireceğidir (daha sonra başka bir seçenek kullanan bir alternatife geri döneceğim). En fazla 16 parça ile bu, en fazla 4 bit gerektirir. Bir oyuncu taş kaybettikçe, seçenek sayısı azalır. Ayrıca belirli bir oyun durumu, taş seçimini sınırlayabilir. Şah kendini kontrol altına almadan hareket edemezse, seçeneklerin sayısı bir azalır. Bir kral kontrol altındaysa, kralı kontrolden çıkaramayan herhangi bir taş geçerli bir seçim değildir.

Parça belirtildikten sonra, yalnızca belirli sayıda yasal varış noktasına sahip olacaktır. Kesin sayı, büyük ölçüde tahta düzenine ve oyun geçmişine bağlıdır, ancak belirli maksimum değerleri ve beklenen değerleri bulabiliriz. Şövalye hariç herkes için ve rok atma sırasında, bir taş başka bir taştan geçemez. Bu, büyük bir hareket sınırları kaynağı olacak, ancak ölçülmesi zor. Bir parça tahtadan hareket edemez ve bu da varış noktalarının sayısını büyük ölçüde sınırlar.

Çoğu parçanın hedefini, aşağıdaki sırayla çizgiler boyunca kareleri numaralandırarak kodluyoruz: W, NW, N, NE (siyah taraf N'dir). Verilen yönde en uzaktaki karede hareket etmesi yasal olan bir çizgi başlar ve. Engelsiz bir şah için hamle listesi W, E, NW, SE, N, S, NE, SW şeklindedir. At için 2W1N ile başlayıp saat yönünde ilerliyoruz; hedef 0, bu sıradaki ilk geçerli hedeftir.

  • Piyonlar: Hareketsiz bir piyonun 2 hedef seçeneği vardır, bu nedenle 1 bit gerektirir. Bir piyon normal veya geçerken başka bir piyon yakalayabiliyorsa (durumu takip ettiği için kod çözücü bunu belirleyebilir), ayrıca 2 veya 3 hamle seçeneğine sahiptir. Bunun dışında, bir piyon bit gerektirmeden yalnızca 1 seçeneğe sahip olabilir. Bir piyon 7 olduğunda inci rütbe, biz de promosyon seçimine çakmak. Piyonlar genellikle kraliçelere, ardından da atlara terfi ettiğinden, seçenekleri şu şekilde kodluyoruz:
    • kraliçe: 0
    • şövalye: 10
    • piskopos: 110
    • kale: 111
  • Bishop: 4 bit için {d, e} {4,5} olduğunda en fazla 13 hedef.
  • Kale: en fazla 14 hedef, 4 bit.
  • Şövalyeler: en fazla 8 hedef, 3 bit.
  • Kings: Rok yapmak bir seçenek olduğunda, kral S'ye geri döner ve aşağı doğru hareket edemez; bu toplam 7 varış noktası verir. Zamanın geri kalanında, bir şahın en fazla 8 hamlesi vardır ve maksimum 3 bit verir.
  • Vezir: 5 bitlik toplam 27 seçenek için fil veya kale seçenekleriyle aynı

Seçenek sayısı her zaman ikinin üssü olmadığından, yukarıdakiler hala bit israfına neden olur. Seçenek sayısının C olduğunu ve belirli seçeneğin c olarak numaralandırıldığını ve n = ceil (lg ( C )) (seçimi kodlamak için gereken bit sayısı) olduğunu varsayalım . Bir sonraki seçimin ilk bitini inceleyerek, aksi takdirde boşa giden bu değerleri kullanırız. 0 ise, hiçbir şey yapmayın. Bu 1 ve varsa C + C <2 , n , daha sonra, C için c . Bir sayının kodunun çözülmesi bunu tersine çevirir: eğer alınan c > = C ise , C'yi çıkarın ve sonraki sayı için ilk biti 1'e ayarlayın. c<2n - C , ardından sonraki sayı için ilk biti 0'a ayarlayın. 2 n - C <= c < C ise , hiçbir şey yapmayın. Bu şemaya "doygunluk" deyin.

Kodlamayı kısaltabilecek başka bir olası seçim türü, rakibin yakalanacak taşını seçmektir. Bu, bir hamlenin ilk bölümü için, en fazla ek bir bit için seçim sayısını artırır (tam sayı, mevcut oyuncunun kaç parçayı hareket ettirebileceğine bağlıdır). Bu seçimin ardından, verilen oyuncu taşlarından herhangi birinin hamle sayısından muhtemelen çok daha küçük olan bir ele geçirme parçası seçimi gelir. Bir taşa, herhangi bir kardinal yönden yalnızca bir taş artı toplamda en fazla 10 atak yapan taş olmak üzere atlar saldırabilir; Bu, bir yakalama hareketi için toplam maksimum 9 bit verir, ancak ortalama olarak 7 bit beklerim. Bu, özellikle kraliçe tarafından yakalanmalar için avantajlı olacaktır, çünkü çoğu zaman oldukça az yasal varış noktası olacaktır.

Doygunluk ile, yakalama kodlaması muhtemelen bir avantaj sağlamaz. Kullanılan ilk durumda belirterek her iki seçeneğe de izin verebiliriz. Doygunluk kullanılmazsa, oyun kodlaması oyun sırasında seçenekleri değiştirmek için kullanılmayan seçim numaralarını da ( C <= c <2 n ) kullanabilir. Ne zaman C ikinin gücü olursa, seçenekleri değiştiremeyiz.


1

Thomas, kartı kodlamak için doğru yaklaşıma sahip. Ancak bu, ralu'nun hareketleri depolama yaklaşımı ile birleştirilmelidir. Tüm olası hareketlerin bir listesini yapın, bu sayıyı ifade etmek için gereken bit sayısını yazın. Kod çözücü aynı hesaplamayı yaptığından, kaç tane mümkün olduğunu ve kaç biti okuyacağını bildiğinden, uzunluk kodlarına gerek yoktur.

Böylece parçalar için 164 bit, roketleme bilgisi için 4 bit (bir oyunun bir parçasını depoladığımızı varsayarsak, aksi takdirde yeniden yapılandırılabilir), geçerken uygunluk bilgisi için 3 bit elde ederiz - sadece hareketin gerçekleştiği sütunu saklayın ( Geçerken alma mümkün değilse, mümkün olmayan bir sütun depolayın - bu tür sütunlar bulunmalıdır) ve kimin taşınacağı için 1.

Hareketler tipik olarak 5 veya 6 bit alır ancak 1 ile 8 arasında değişebilir.

Bir ek kısayol - eğer kodlama 12 1 bit ile başlıyorsa (geçersiz bir durum - bir parçanın bir tarafında iki papaz bile olmaz) kod çözmeyi iptal eder, tahtayı siler ve yeni bir oyun kurarsınız. Bir sonraki parça biraz hareket olacak.


1

Algoritma, her harekette olası tüm hedefleri belirleyici olarak numaralandırmalıdır. Hedef sayısı:

  • 2 fil, her biri 13 hedef = 26
  • 2 kale, her biri 14 hedef = 28
  • 2 at, her biri 8 hedef = 16
  • kraliçe, 27 destinasyon
  • kral, 8 hedef

8 pençenin tümü en kötü durumda (numaralandırma açısından) kraliçe haline gelebilir, böylece en fazla sayıda olası varış yeri 9 * 27 + 26 + 28 + 16 + 8 = 321 olur. Böylece, herhangi bir hareket için tüm hedefler 9 bitlik bir sayı ile numaralandırılabilir.

Her iki tarafın da maksimum hamle sayısı 100'dür (yanılmıyorsam satranç oyuncusu değilsem). Böylece herhangi bir oyun 900 bit olarak kaydedilebilir. Artı başlangıç ​​konumu, her bir parça toplamda 32 * 6 = 192 bit olan 6 bitlik sayılar kullanılarak kaydedilebilir. Artı "ilk kim hamle" kaydı için bir bit. Böylece 900 + 192 + 1 = 1093 bit kullanılarak herhangi bir oyun kaydedilebilir.


1

Yönetim kurulu durumunun kaydedilmesi

Düşündüğüm en basit yol, ilk önce her bir parçanın konumunu temsil eden 8 * 8 bitlik bir diziye sahip olmaktır (Yani, orada bir satranç taşı varsa 1 ve yoksa 0). Bu sabit bir uzunluk olduğu için herhangi bir sonlandırıcıya ihtiyacımız yok.

Sonra her satranç taşını bulunduğu yere göre temsil edin. Parça başına 4 bit kullanarak, bu 32 * 4 bit (toplam 128) alır. Bu gerçekten savurgan.

İkili ağaç kullanarak, bir baytta bir piyon, 3'te bir at ve kale ve fili ve 4'te bir şah ve veziri temsil edebiliriz. Ayrıca fazladan bir bayt alan parçanın rengini de saklamamız gerektiğinden, sonunda sona erer. olarak (eğer bu yanlışsa beni affet, daha önce Huffman kodlamasına hiç detaylı bakmadım ):

  • Piyon: 2
  • Kale: 4
  • Şövalye: 4
  • Piskopos: 4
  • Kral: 5
  • Kraliçe: 5

Toplamlara göre:

2*16 + 4*4 + 4*4 + 4*4 + 2*5 + 2*5 = 100

Sabit boyutlu bit setini 28 bit kullanarak yener.

Bu yüzden bulduğum en iyi yöntem onu ​​8 2 + 100 bitlik bir dizide saklamaktır.

8*8 + 100 = 164



Hareketleri Kaydetme
Bilmemiz gereken ilk şey, hangi parçanın nereye gittiğidir. Tahtada en fazla 32 parça olduğu ve kareyi temsil eden bir tamsayı yerine her parçanın ne olduğunu bildiğimiz için, parça ofsetini temsil eden bir tam sayıya sahip olabiliriz, bu da bir parça.

Maalesef kralı devirmek ya da devirmek ve bir cumhuriyet kurmak gibi çeşitli özel kurallar vardır (Terry Pratchett referansı), bu nedenle taşınacak parçayı saklamadan önce özel bir hareket olup olmadığını gösteren tek bir parçaya ihtiyacımız var.

Yani her normal hareket için gerekli 1 + 5 = 6bitlere sahibiz . (1 bit tipi, parça için 5 bit)

Parça numarasının kodu çözüldükten sonra, parçanın türünü biliyoruz ve her bir parça, hareketini en verimli şekilde temsil etmelidir. Örneğin (satranç kurallarım sıfırlanıyorsa) bir piyonun toplam 4 olası hamlesi vardır (sola dön, sağa git, bir ileri git, iki ileri git).
Yani bir piyon hareketini temsil etmek için '6 + 2 = 8' bitine ihtiyacımız var. (İlk hareket başlığı için 6 bit, hangi hareket için 2 bit)

Kraliçe için hareket etmek daha karmaşık olacaktır, çünkü bir yöne (8 olası yön, yani 3 bit) ve her yön için hareket etmek için toplam 8 olası kareye (yani 3 bit daha) sahip olmak en iyisidir. Yani bir vezir hareketini temsil etmek için 6 + 3 + 3 = 12bitlere ihtiyaç vardır .

Aklıma gelen son şey, hangi oyuncuların onu çevirdiğini kaydetmemiz gerektiğidir. Bu tek bir bit olmalıdır (sonraki hareket için beyaz veya siyah)



Sonuç Biçimi
Böylece dosya biçimi şuna benzer

[64 bit] İlk parça konumları
[100 bit maks.] İlk parçalar [1 bit] Oyuncu dönüşü
[n bit] Hareketler

Hareketin
[1 bit] olduğu yerde Hareket türü (özel veya normal)
[n bit] Taşı Detayı

Hareket normal bir hareketse, Taşı Detayı
[5 bit] parça
[n bit] belirli bir parça hareketine benzer (genellikle 2 ila 6 bit aralığında]

Özel bir hamle ise
, bir tamsayı türüne ve daha sonra herhangi bir ek bilgiye (rok atma gibi) sahip olmalıdır. Özel hamlelerin sayısını hatırlayamıyorum, bu yüzden bunun özel bir hareket olduğunu belirtmek sorun olabilir (eğer sadece bir tane varsa)


1

İlk panonun temel durumunda ve sonraki hamlelerde, aşağıdakileri göz önünde bulundurun.

Tüm olası hareketlere olasılıklar atamak için bir satranç programı kullanın. Örneğin, e2-e4 için% 40, d2-d4 için% 20 vb. Bazı hareketler yasalsa ancak bu program tarafından dikkate alınmıyorsa, onlara düşük olasılık verin. Hangi seçimin yapıldığını kaydetmek için aritmetik kodlamayı kullanın; bu, ilk hareket için 0 ile 0,4 arasında, ikinci için 0,4 ile 0,6 arasında bir sayı olacaktır, vb.

Diğer taraf için de aynısını yapın. Örneğin, e2-e4'e yanıt olarak% 50 e7-e5 şansı varsa, kodlanan sayı 0 ile 0.2 arasında olacaktır. Oyun bitene kadar tekrarlayın. Sonuç, potansiyel olarak çok küçük bir aralıktır. Bu aralığa uyan en küçük tabana sahip ikili kesri bulun. Bu aritmetik kodlamadır.

Bu, Huffman'dan daha iyidir çünkü kesirli bit kodlaması olarak düşünülebilir (artı bir kısmı oyunun sonunda bir bite yuvarlanır).

Sonuç Huffman'dan daha derli toplu olmalı ve terfi, geçerken alma, 50 kuralı hamlesi ve diğer ayrıntılar için özel durumlar yoktur çünkü bunlar satranç değerlendirme programı tarafından ele alınır.

Tekrar oynamak için, tahtayı değerlendirmek ve her hamleye tüm olasılıkları atamak için tekrar satranç programını kullanın. Gerçekte hangi hareketin oynandığını belirlemek için aritmetik olarak kodlanmış değeri kullanın. Bitene kadar tekrarlayın.

Satranç programınız yeterince iyiyse, olasılıkların hem siyah hem de beyaz için hamlelere göre tanımlandığı iki durumlu bir kodlayıcı ile daha iyi sıkıştırma elde edebilirsiniz. 200'den fazla eyaletteki en uç durumda, bu, olası tüm satranç oyunlarının tamamını kodlar ve bu nedenle uygulanabilir değildir.

Bu, Darius'un daha önce yazdıklarını söylemenin oldukça farklı bir yolu, sadece aritmetik kodlamanın nasıl çalıştığına dair bir parça örnek ve bir sonraki hamle (ler) in olasılığını değerlendirmeye yardımcı olmak için mevcut bir satranç programını kullanmanın gerçek bir örneğiyle.

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.