プログラマーパズル:ゲーム全体でチェス盤の状態をエンコードする


95

厳密には質問ではなく、パズルのようなものです...

長年にわたり、私は新入社員の技術面接にいくつか携わってきました。「Xテクノロジーを知っていますか」という標準的な質問をする以外に、私は彼らが問題にどのように取り組むかについての感触を得ることも試みました。通常、面接の前日にメールで質問を送信し、翌日までに解決策が見つかると期待します。

多くの場合、結果は非常に興味深いものになります-間違っていますが、興味深いものです。その人が特定のアプローチをとった理由を説明できれば、その人は私の推奨を得るでしょう。

だから私はスタックオーバーフローの聴衆に向けて私の質問の1つを投げると思いました。

質問:チェスゲーム(またはそのサブセット)の状態をエンコードするために考えられる最もスペース効率の良い方法は何ですか?つまり、駒が合法的に配置されたチェス盤が与えられた場合、この初期状態と、ゲーム内のプレーヤーが取ったその後のすべての合法的な動きの両方をエンコードします。

答えに必要なコードはなく、使用するアルゴリズムの説明のみです。

編集:ポスターの1つが指摘したように、私は移動の時間間隔を考慮しませんでした。オプションの追加としてそれについても説明してください:)

EDIT2:補足説明のために...エンコーダー/デコーダーはルールに対応していることを忘れないでください。実際に保存する必要があるのはプレーヤーの選択だけです。それ以外は、エンコーダー/デコーダーが認識していると見なすことができます。

EDIT3:ここで勝者を選ぶのは難しいでしょう:)たくさんの素晴らしい答え!


4
チェスゲームの初期状態は明確に定義されていませんか?なぜエンコードする必要があるのですか?各ターン(=ムーブ)間の差分のみをエンコードするだけで十分だと思います。
tanascius 2009

1
彼は、ゲームが合法的な初期設定で開始できると想定しています(新聞で見つけることができるチェスのゲームパズルのように)。
アーロンディグラ2009

6
同じ位置が3回表示された場合、それはドローだから厳密であることを、あなたはまた、すべての過去の位置をエンコードする必要がありますen.wikipedia.org/wiki/Threefold_repetition
フライ・バイ・ワイヤ

4
提案:これは、人々がエントリーをプログラムとして提出する本物のコンテストにしてください。プログラムは、チェスゲームを入力として(人間が読める、最適化されていない基本的な形式を定義できます)、圧縮されたゲームを出力します。次に、パラメーターを使用して、圧縮されたゲームを取得し、一致する必要がある元の入力を再生成します。
Vilx- 2009

2
さらに言えば、指示に従うことができないことを示しています...ほとんどのubercoderでさえ、ある時点で指示に従う必要があります。ある方法で何かを実装するように言われた状況に出くわしました。それは愚かな実装だと思った(そして言った)のですが、それが判明したときに顔に卵が残されるだけでした。それをそのように実装させたのには非常に正当な理由がありました(私が知らなかった、または理解していなかった)。
Andrew Rollings

回答:


132

更新:私はこのトピックがとても好きだったので、プログラミングパズル、チェスの位置、ハフマンコーディングを書きました。これを一読すると、完全なゲーム状態を保存する唯一の方法は、ムーブの完全なリストを保存することであると判断しました。その理由を読んでください。そこで、ピースレイアウトの問題を少し簡略化したバージョンを使用します。

問題

この画像は、チェスの開始位置を示しています。チェスは8x8のボードで行われ、各プレーヤーは、8つのポーン、2つのルーク、2つのナイト、2つのビショップ、1つのクイーン、1つのキングで構成される16個のピース​​の同じセットから始まります。

チェスの開始位置

通常、位置は列の文字として記録され、その後に行の番号が続くため、ホワイトのクイーンはd1にあります。ほとんどの場合、移動は代数表記で保存されます。これは明確であり、通常、必要な最小限の情報のみを指定します。この開口部を検討してください:

  1. e4 e5
  2. Nf3 Nc6

これは次のように変換されます。

  1. 白はキングのポーンをe2からe4に移動します(これはe4に到達できる唯一のピースなので、「e4」)。
  2. 黒はキングのポーンをe7からe5に移動します。
  3. 白は騎士(N)をf3に移動します。
  4. 黒は騎士をc6に移動します。

ボードは次のようになります。

早開き

プログラマにとって重要な機能は、問題正確かつ明確に特定できることです。

では、何が欠けていたり、あいまいであったりしますか?結局のところ、たくさん。

ボードの状態とゲームの状態

最初に決定する必要があるのは、ゲームの状態を保存しているか、ボード上の駒の位置を保存しているかです。ピースの位置を単純にエンコードすることは1つのことですが、問題は「その後のすべての合法的な動き」を示しています。問題は、この時点までの動きを知ることについても何も言っていません。私が説明するように、それは実際には問題です。

キャスリング

ゲームは次のように進行しました:

  1. e4 e5
  2. Nf3 Nc6
  3. Bb5 a6
  4. Ba4 Bc5

ボードは次のようになります。

後で開く

ホワイトはキャスリングのオプションがあります。これに対する要件の一部は、キングと関連するルークが決して移動できないことです。そのため、キングまたは各サイドのルークが移動したかどうかを保存する必要があります。明らかに、それらが開始位置にない場合、それらは移動しているので、指定する必要があります。

この問題に対処するために使用できるいくつかの戦略があります。

まず、追加の6ビットの情報(ルークとキングごとに1つ)を保存して、駒が移動したかどうかを示すことができます。正しいピースがたまたまある場合、これらの6つの正方形の1つにビットを格納するだけでこれを合理化できます。別の方法として、動かない各ピースを別のピースタイプとして扱うこともできます。つまり、6つのピースタイプ(ポーン、ルーク、ナイト、ビショップ、クイーン、キング)の代わりに、8(動かないルークと動かないキングを追加)があります。

En Passant

チェスのもう1つの独特でしばしば無視されがちなルールはEn Passantです。

en passant

ゲームが進行しました。

  1. e4 e5
  2. Nf3 Nc6
  3. Bb5 a6
  4. Ba4 Bc5
  5. OO b5
  6. Bb3 b4
  7. c4

b4の黒のポーンは、b4の彼のポーンをc3に移動して、c4の白のポーンを取るオプションがあります。これは最初の機会でのみ発生します。つまり、ブラックがオプションを渡した場合、彼は次の行動を取ることができなくなります。これを保存する必要があります。

前の動きがわかっていれば、En Passantが可能かどうか間違いなく答えることができます。別の方法として、4番目のランクの各ポーンが2倍前進してそこに移動したかどうかを保存することもできます。または、ボード上で可能なすべてのEn Passantの位置を確認し、可能かどうかを示すフラグを設定できます。

昇進

ポーンプロモーション

ホワイトの動きです。ホワイトがh7からh8にポーンを移動する場合、他の駒に昇格できます(ただし、キングは不可)。99%の確率でクイーンに昇格しますが、そうでない場合もあります。通常は、そうしないと勝つと、行き詰まってしまうからです。これは次のように書かれます:

  1. h8 = Q

これは私たちの問題にとって重要です。なぜなら、それは、それぞれの側に一定数のピースがあることに頼ることができないことを意味するからです。8つのポーンすべてが昇格した場合、片方がクイーン9、ルーク10、ビショップ10、またはナイト10になる可能性はまったくありません(信じられないほどありません)。

行き詰まり

あなたが最善の戦術を勝つことができない立場にいるときは、行き詰まりを試すことです。最も可能性の高いバリアントは、法的な移動ができない場所です(通常、キングをチェックしたときに移動するため)。この場合、あなたは引き分けを主張することができます。これは簡単に対応できます。

2番目のバリアントは、3回の繰り返しによるものです。ゲームで同じボードの位置が3回発生した場合(または次の手で3回目に発生した場合)、ドローを要求できます。位置は特定の順序で発生する必要はありません(つまり、同じ順序で3回繰り返す必要はありません)。以前のボードの位置をすべて覚えておく必要があるため、これは問題を非常に複雑にします。これが問題の要件である場合、問題の唯一の可能な解決策は、以前のすべての移動を保存することです。

最後に、50の移動ルールがあります。ポーンが移動せず、前の50回の連続した移動で駒が1つも取られなかった場合、プレーヤーはドローを要求できます。そのため、ポーンが移動した後の移動数または駒(2つのうち最新のもの)を保存する必要があります。 6ビット(0-63)。

誰の番ですか?

もちろん、それが誰であるかを知る必要もあります。これはほんの少しの情報です。

2つの問題

行き詰まったケースのため、ゲームの状態を保存する唯一の実行可能なまたは賢明な方法は、この位置に至るすべての動きを保存することです。その1つの問題に取り組みます。ボードの状態の問題はこれに単純化されます:キャスティング、エンパッサント、ステイルメイトの条件とその順番を無視して、ボード上のすべてのピースの現在の位置を保存します。

ピースのレイアウトは、各正方形のコンテンツを格納するか、各ピースの位置を格納するという2つの方法のいずれかで広く処理できます。

シンプルなコンテンツ

駒のタイプは6つあります(ポーン、ルーク、ナイト、ビショップ、クイーン、キング)。各ピースは白または黒にすることができるので、正方形には12の可能なピースの1つが含まれるか、または空である可能性があるため、13の可能性があります。13は4ビット(0〜15)で格納できます。したがって、最も簡単な解決策は、各平方に64ビットの256ビットまたは256ビットの情報を乗算して4ビットを格納することです。

この方法の利点は、操作が非常に簡単で高速であることです。これは、ストレージ要件を増やすことなく、さらに3つの可能性を追加することで拡張できます。最後のターンに2スペース移動したポーン、移動していないキング、移動していないルーク。前述の問題の。

しかし、私たちはもっとうまくやることができます。

Base 13エンコーディング

多くの場合、ボードの位置を非常に大きな数として考えると役立ちます。これはコンピュータサイエンスでよく行われます。たとえば、停止の問題は、コンピュータプログラムを(正しく)大きな数として扱います。

最初のソリューションは位置を64桁のベース16番号として扱いますが、実証されているように、この情報には冗長性があり(「桁」ごとに3つの未使用の可能性があるため)、番号スペースを64桁の13桁に減らすことができます。もちろん、これはbase 16ほど効率的に行うことはできませんが、ストレージ要件を節約できます(そして、ストレージスペースを最小限に抑えることが目標です)。

10進数では、234は2 x 10 2 + 3 x 10 1 + 4 x 10 0に相当します

基数16では、数値0xA50は10 x 16 2 + 5 x 16 1 + 0 x 16 0 = 2640(10進数)に相当します。

したがって、位置をp 0 x 13 63 + p 1 x 13 62 + ... + p 63 x 13 0としてエンコードできますここで、p iは正方形iの内容を表します。

2 256は約1.16e77に相当します。13 64は約1.96e71に相当し、237ビットのストレージスペースが必要です。わずか7.5%の節約は、操作コストの大幅な増加という犠牲を伴います。

可変ベースエンコーディング

法定掲示板では、特定のマスが特定の正方形に表示されない場合があります。たとえば、第1ランクまたは第8ランクではポーンが発生せず、これらの正方形の可能性が11に減少します。これにより、可能なボードが11 16 x 13 48 = 1.35e70(約)に減少し、233ビットのストレージスペースが必要になります。

実際にそのような値を10進数(または2進数)にエンコードおよびデコードすることは少し複雑ですが、確実に実行でき、読者への課題として残されています。

可変幅のアルファベット

前の2つの方法はどちらも、固定幅のアルファベットエンコーディングとして説明できます。アルファベットの11、13、または16の各メンバーは、別の値に置き換えられます。各「文字」の幅は同じですが、各文字の可能性が同じではないと考えると、効率が向上します。

モールス信号

モールス符号を考えてみてください(上図)。メッセージ内の文字は、ダッシュとドットのシーケンスとしてエンコードされます。それらのダッシュとドットは、それらを区切るためにそれらの間に一時停止を置いて(通常)無線で転送されます。

文字E(英語で最も一般的な文字)が単一のドット、つまり最短のシーケンスであるのに対し、Z(頻度が最も低い)は2つのダッシュと2つのビープ音です。

このようなスキームでは、予想されるメッセージのサイズを大幅に縮小できますが、ランダムな文字シーケンスのサイズを大きくするという犠牲が伴います。

モールス符号には別の組み込み機能があることに注意してください。ダッシュは3つのドットと同じ長さなので、ダッシュの使用を最小限に抑えるために上記のコードはこれを念頭に置いて作成されます。1と0(ビルディングブロック)にはこの問題がないため、これを複製する必要のある機能ではありません。

最後に、モールス符号には2種類の休符があります。短い休符(ドットの長さ)は、ドットとダッシュを区別するために使用されます。長いギャップ(ダッシュの長さ)は、文字を区切るために使用されます。

では、これは私たちの問題にどのように当てはまりますか?

ハフマンコーディング

ハフマンコーディングと呼ばれる可変長コードを処理するためのアルゴリズムがあります。ハフマンコーディングは、可変長のコード置換を作成します。通常、シンボルの予想頻度を使用して、より一般的なシンボルに短い値を割り当てます。

ハフマンコードツリー

上記のツリーでは、文字Eは000(またはleft-left-left)としてエンコードされ、Sは1011です。このエンコードスキームはあいまいでないこと明らかです。

これはモールス符号との重要な違いです。モールス符号には文字セパレーターがあるため、それ以外の場合はあいまいな置換を行うことができます(たとえば、4つのドットはHまたは2 Isになる可能性があります)が1と0しかないため、代わりに明確な置換を選択します。

以下は簡単な実装です:

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; }
}

静的データあり:

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);
}

そして:

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);
    }
  }
}

考えられる出力は次のとおりです。

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

開始位置の場合、これは32 x 1 + 16 x 3 + 12 x 5 + 4 x 6 = 164ビットに相当します。

状態の違い

別の可能なアプローチは、最初のアプローチをハフマンコーディングと組み合わせることです。これは、(ランダムに生成されたものではなく)最も期待されるチェス盤は、少なくとも部分的には、開始位置に似ている可能性が高いという仮定に基づいています。

したがって、実行するのは、256ビットの現在のボード位置と256ビットの開始位置をXORしてから、それをエンコードします(ハフマンコーディングまたは、たとえば、ランレングスエンコーディングのいくつかの方法を使用します)。明らかに、これは(64ビットに対応する64 0)から始めるのは非常に効率的ですが、ゲームが進むにつれて必要なストレージが増加します。

ピースポジション

前述のように、この問題を攻撃する別の方法は、代わりにプレイヤーが持っている各駒の位置を保存することです。これは、ほとんどの正方形が空になるエンドゲームの位置で特にうまく機能します(ただし、ハフマンコーディングアプローチでは、空の正方形はとにかく1ビットしか使用しません)。

各サイドには、キングと0〜15個の他のピースがあります。プロモーションのため、これらのピースの正確な構成は、開始位置に基づく数が最大であると想定できないほど十分に異なる場合があります。

これを分割する論理的な方法は、2つの面(白と黒)で構成される位置を保存することです。両サイドには以下があります:

  • キング:ロケーションの6ビット。
  • ポーンあり:1(はい)、0(いいえ)。
  • はいの場合、ポーンの数:3ビット(0-7 + 1 = 1-8)。
  • はいの場合、各ポーンの位置がエンコードされます:45ビット(以下を参照)。
  • 非ポーンの数:4ビット(0-15)。
  • 各ピース:タイプ(クイーン、ルーク、ナイト、ビショップの2ビット)と場所(6ビット)

ポーンの場所については、ポーンは48の可能な四角形にしか配置できません(他の64のようにはできません)。そのため、ポーンごとに6ビットを使用する場合に使用される余分な16値を無駄にしない方がよいでしょう。したがって、ポーンが8つある場合、8つの可能性は48あり、28,179,280,429,056に相当します。その多くの値をエンコードするには45ビットが必要です。

これは、片側あたり105ビットまたは合計210ビットです。開始位置はこの方法の最悪のケースですが、ピースを取り除くと大幅に改善されます。

48の未満があることが指摘されるべきである8ポーンは、すべて最初の48個の可能性、第47などを有する同じ正方形であることができないので、可能性が。48 x 47 x…x 41 = 1.52e13 = 44ビットストレージ。

これをさらに改善するには、他の部分(反対側を含む)が占める正方形を削除して、最初に白の非ポーン、次に黒の非ポーン、次に白のポーン、最後に黒のポーンを配置します。開始位置では、これにより、ストレージ要件が白では44ビットに、黒では42ビットに削減されます。

組み合わせたアプローチ

別の可能な最適化は、これらのアプローチのそれぞれに長所と短所があることです。たとえば、最高の4を選択し、最初の2ビットでスキームセレクターをエンコードし、その後にスキーム固有のストレージをエンコードすることができます。

オーバーヘッドが小さいため、これは断然最善のアプローチです。

ゲームの状態

ポジションではなく、ゲームを保存する問題に戻ります。3回繰り返されているため、この時点までに発生した移動のリストを保存する必要があります。

注釈

決定しなければならないことの1つは、単に移動のリストを保存しているのか、それともゲームに注釈を付けているのかということです。チェスのゲームには、多くの場合、注釈が付けられています。

  1. Bb5 !! NC4?

ホワイトの動きは2つの感嘆符で見事にマークされていますが、ブラックの動きは間違いと見なされています。チェスの句読点を参照してください。

さらに、動きが説明されているので、フリーテキストを保存する必要がある場合もあります。

私は移動が十分であると想定しているため、注釈はありません。

代数表記

ここに移動のテキスト(「e4」、「Bxb5」など)を格納するだけで済みます。移動ごとに約6バイト(48ビット)である終了バイトを含める(最悪の場合)。それは特に効率的ではありません。

2番目に試すことは、開始位置(6ビット)と終了位置(6ビット)を保存して、1移動あたり12ビットにすることです。それはかなり良いです。

または、現在の位置からのすべての合法的な動きを、予測可能かつ確定的な方法で決定し、選択した状態を示すこともできます。その後、上記の可変ベースエンコーディングに戻ります。白と黒は、最初の手でそれぞれ20の可能な手があります。

結論

この質問に対する絶対的に正しい答えはありません。上記のほんの一部である多くの可能なアプローチがあります。

この問題と同様の問題について私が気に入っているのは、使用パターンの検討、要件の正確な決定、コーナーケースの検討など、プログラマーにとって重要な能力を必要とすることです。

Chess Position Trainerからスクリーンショットとして撮ったチェスの位置。


3
そして結果を後でgzip(ヘッダーが結果を増加させない場合); ^)
Toad

黒または白を示すためにスペースを2倍にする必要はありませんか?
ダニエル・エリオット

5
良いポスト。小さな修正:ルークが移動してから戻ってきた可能性があるため、キャスティングには4ビットが必要です。もう少し重要:おそらく誰の動きかを含める必要があります。=)
A.レックス

9
騎士への昇進に関しては、一度行ったことがある。本当に荒々しい状況だった-彼は私を交尾させることからの1つの動きでした、私はそれを止めることができませんでした。彼は私のポーンを無視していました。代わりに騎士に昇格し、彼と交尾したときにカメラがあったらいいのに!
Loren Pechtel、2009

2
[FEN] [1]がキャスリングやenpassantの可用性などを処理していることに言及していないことに驚いています。[1] en.wikipedia.org/wiki/FEN
Ross

48

チェスのゲームを人間が読める標準形式で保存するのが最善です。

ポータブルゲーム表記は、(それがものの、標準の開始位置を想定していなくても)だけ移動を一覧表示し、ターンでターン。人間が読めるコンパクトな標準形式。

例えば

[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

小さくしたい場合は、圧縮してください。仕事完了!


23
2件の反対票に対する私の防御では、これは受け取ったものです。1)それはあなたが望むことを実行します。2)thedailywtf.com/articles/riddle-me-an-interview.aspxテストに合格します: "...解決できる人々の一部これらのなぞなぞは、まさにプログラマーとして望まない人々のタイプです。水移動スケール/バージを構築し、ドックまで747をタクシーで動かし、それを使用してジャンボジェットの重量を測定する人と協力しますか?そもそも単にボーイングに電話する代わりに」面接でランダムエンコーディングを発明した人を雇うことはありません。彼らもコードでそれを行うからです。
Rob Grant、

1
まあ、私が具体的に彼らに問題を解決して彼らの問題解決テクニックを得るために頼んでいるなら、私は他の質問を他の質問でカバーすることを仮定することができます...
Andrew Rollings

7
@reinier:私は情報密度の問題について完全に無知だと言っているわけではありません(あなたは私の返答を無能の容認であると誤解しました)。確かに、既存のデータストレージ標準にコーディングする人を雇う必要があり、独自のツールを使用するのではなく、適切な既存のツールを使用することをお勧めします-「私たちはThe Wheel 2.0を発明しました!今ではさらに丸められています!」ライブラリ関数を使用することが弱さの表れであると奇妙に思う人を雇うのは間違いです。
Rob Grant、

18
これは、インタビューでのこの質問に対する私の最初の答えです。あなたは、最初の本能がすぐに使える解決策を探すことであることを示したいと思います。面接担当者が自分で思いつくものを聞きたいと言った場合、ビットパッキングソリューションに進むことができます。
リザードを請求する

2
私はこれについてロバートと一緒にいます-既存の解決策は実用的で人間が読めるもので十分コンパクトです。それらをデコードするための複雑なアルゴリズムを備えたカスタムスーパーパックソリューションと比較すると、すべてが大きな偉業です。面接の場合は、実践的な側面も考慮します。本当に賢い人が何回超複雑で非実用的な解決策を思いつくかは驚くでしょう。通常、彼らは頭の中で複雑さを処理できるという事実に起因しますが、それから-私たちの残りの部分についてはどうでしょうか
MaR

15

素晴らしいパズル!

ほとんどの人が各ピースの位置を保存しているようです。より単純なアプローチを取り、各正方形の内容を保存してみませんか?これにより、プロモーションとキャプチャされたピースが自動的に処理されます。

そして、それはハフマンエンコーディングを可能にします。実際には、ボード上の駒の初期頻度はほぼ完璧です。正方形の半分は空で、残りの正方形の半分はポーンなどです。

各部分の頻度を考慮して、紙にハフマンツリーを作成しましたが、ここでは繰り返しません。結果、cは色を表します(白= 0、黒= 1):

  • 空の正方形の場合は0
  • ポーンの場合は1c0
  • ルーク用1c100
  • 1c101騎士
  • ビショップの場合は1c110
  • クイーンの場合は1c1110
  • 王のための1c1111

初期状態のボード全体について、

  • 空の正方形:32 * 1ビット= 32ビット
  • ポーン:16 * 3ビット= 48ビット
  • ルーク/ナイト/ビショップ:12 * 5ビット= 60ビット
  • クイーン/キング:4 * 6ビット= 24ビット

合計:ボードの初期状態では164ビット。現在最も投票数の多い回答の235ビットを大幅に下回っています。また、ゲームが進むにつれて(プロモーション後を除いて)小さくなるだけです。

私はボード上の駒の位置だけを見ました。追加の状態(そのターン、キャッスルしている、エンパッサン、繰り返し移動など)は、個別にエンコードする必要があります。多分最大16ビットなので、ゲーム状態全体で180ビットです。可能な最適化:

  • 頻度の低い部分を除外し、位置を個別に保存します。しかし、それは役に立ちません... kingとqueenを空の正方形で置き換えると、5ビット節約できます。これは、別の方法で位置をエンコードするために必要な5ビットです。
  • 「後列にはポーンはありません」は、後列に別のハフマンテーブルを使用することで簡単にエンコードできますが、それが大いに役立つとは思えません。あなたはおそらく同じハフマンツリーになってしまうでしょう。
  • 「白が1つ、黒が1つのビショップ」は、cビットを持たない追加のシンボルを導入することでエンコードでき、ビショップが配置されている正方形から推定できます。(司教に昇格したポーンはこのスキームを混乱させます...)
  • 空の正方形の繰り返しは、たとえば「行に2つの空の正方形」と「行に4つの空の正方形」のシンボルを追加することで、ランレングスエンコードできます。しかし、それらの頻度を見積もるのはそれほど簡単ではありません。もしそれを間違ってしまった場合、助けになるのではなく、傷つけてしまいます。

銀行ランクのポーンはビットを節約しません-他のすべてのパターンからビット#3を切り刻むことができます。したがって、実際には銀行ランクで1個あたり1ビット節約できます。
Loren Pechtel、2009

2
64個の正方形のそれぞれに対して個別のハフマンツリーを実行できます。これは、一部の断片が他の断片よりも頻繁に存在する可能性があるためです。
Claudiu

9

本当に大きなルックアップテーブルアプローチ

位置 -18バイト
推定される正当な位置の数は 10 43です。
単純にそれらすべてを列挙するだけで、位置を143ビットで保存できます。次に再生する側を示すために、もう1ビットが必要です

列挙はもちろん実用的ではありませんが、これは少なくとも144ビットが必要であることを示しています。

移動 -1バイト
通常、各ポジションには30〜40の正当な移動がありますが、その数は最大218になる場合があります218各ポジションのすべての正当な移動を列挙します。これで、各移動を1バイトにエンコードできます。

辞任を表す0xFFなどの特別な移動の余地はまだ十分にあります。


3
「チェスのゲームの状態をエンコードするために考えることができる最もスペース効率の良い方法」という要件の中心に直接-何かを潰すのに辞書よりも優れたものはなく、フライも含まれます。
アンドリュー、

1
そのような辞書を生成するのにかかる時間についての興味深いリンクを見つけました:) ioannis.virtualcomposer2000.com/math/EveryChess.html
Andrew Rollings

Shannonsの見積もりは少し時代遅れです:-)彼はプロモーションもキャプチャも含まれていませんでした。上位5×10 ^ 52の拘束ビクター・アリス1994年で与えられた
ギュンターPiez

確かに可変長エンコーディングでは、平均のみが少なくとも10 ^ 43ですか?より多くの位置に偏ったエンコーディングでは、特に位置の多くが不可能であるため、これを減らす必要があります。
Phil H

EveryChessリンクは現在「販売」されています
。archive.org

4

最悪のケースではなく、人間がプレイする典型的なゲームの平均ケースサイズを最適化することに関心が追加されます。(問題の説明にはどちらが記載されていないか、ほとんどの応答は最悪の場合を想定しています。)

移動シーケンスについては、優れたチェスエンジンで各位置から移動を生成します。品質の順位で並べられた、k通りの可能な動きのリストを生成します。人々は通常、ランダムな動きよりも良い動きを選ぶ頻度が高いため、リストの各位置から人々が「良い」動きを選ぶ確率までのマッピングを学習する必要があります。これらの確率(インターネットチェスデータベースのゲームのコーパスに基づく)を使用して、算術コーディングで動きをエンコードします。(デコーダーは同じチェスエンジンとマッピングを使用する必要があります。)

開始位置としては、ラルのアプローチが有効です。確率によって選択に重みを付ける何らかの方法があれば、そこで算術コーディングを使用してそれを改良することもできます。たとえば、ピースはランダムではなく、互いに防御する構成でしばしば表示されます。その知識を組み込む簡単な方法を見つけるのは難しいです。1つのアイデア:標準の開始位置から始めて、目的のボードで終わるシーケンスを見つける代わりに、上記の移動エンコーディングにフォールバックします。(最終的な位置からのピースの距離の合計に等しいヒューリスティック距離、またはそれらの線に沿った何かでA *検索を試行する場合があります。)これは、移動シーケンスの過剰指定による非効率性とチェスプレイの利点による効率性のトレードオフです。知識。

また、実際のコーパスから統計情報を収集せずに、平均的なケースの複雑さでどれほどの節約ができるかを見積もることも、一種の困難です。しかし、すべての動きが等しく可能性の高い開始点は、ここでのほとんどの提案をすでに上回っていると思います。算術符号化は、動きごとに整数のビット数を必要としません。


この情報をプールに格納する複雑さはO(n)です。編集した回答を確認してください。
Luka Rahne、

ralu、あなたの言っていることはわかりませんが、もしあなたが一連の動きの表現が最悪の場合に最適な空間を使用することを意味しているなら、私はそれと矛盾しません。ここでのアイデアは、他よりも可能性が高いいくつかの動きを利用することです。
ダライアスベーコン

よりリクリーなポジションを見つけるために必要なのは、利用可能な動きを与えられたポジションの確定的な方法でソートする確定的な(そして強力な)チェスエンジンを使用することだけです。
Luka Rahne、

4

初期位置がエンコードされた後のステップをエンコードする副問題への攻撃。アプローチは、ステップの「リンクされたリスト」を作成することです。

ゲームの各ステップは、「古い位置->新しい位置」のペアとしてエンコードされます。あなたはチェスのゲームの最初の最初の位置を知っています。リンクされたステップのリストをトラバースすることにより、Xが移動した後の状態に移動できます。

各ステップをエンコードするには、開始位置をエンコードするために64個の値が必要です(ボード上の64スクエアの場合は6ビット-8x8スクエア)、および終了位置の場合は6ビット。片側1移動あたり16ビット。

与えられたゲームをエンコードするのに必要なスペースの量は、動きの数に比例します:

10 x(白の移動数+黒の移動数)ビット。

更新:促進されたポーンとの潜在的な合併症。ポーンが昇格する対象を示すことができる必要があります-特別なビットが必要な場合があります(ポーンの昇格は非常にまれであるため、スペースを節約するために灰色のコードを使用します)。

更新2:終了位置の完全な座標をエンコードする必要はありません。ほとんどの場合、移動されるピースはX箇所までしか移動できません。たとえば、ポーンは任意の時点で最大3つの移動オプションを持つことができます。各ピースタイプの最大移動数を実現することで、「宛先」のエンコーディングのビットを節約できます。

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

したがって、黒または白の動きごとの空間的複雑性は

初期位置の6ビット+(移動するもののタイプに基づく可変ビット数)。


更新されたばかりで、128の組み合わせを意味します-明らかに128ビット未満です:) :)
Alex Weinstein

1
ゲームの状態は手と同じではありません。任意の位置は頂点またはノードと考えることができ、合法的な移動は有向エッジまたは矢印と考えることができ、(有向非循環)グラフを形成します。
シャギーフロッグ

なぜ反対票が投じられたのかはわかりません。更新されたアイデアについての人々の意見を聞きたいです。
Alex Weinstein、

1
これはあなたの推論には影響しませんが、小さな修正です:ポーンはプロモーションを含まない4つの動き、またはプロモーションを含む12つの動きを持つことができます。e2のポーンの例:e3、e4、exd3、exf3。e7でのポーンの例:e8Q、e8N、e8R、e8B、exd8Q、exd8N、exd8R、exd8B、exf8Q、exf8N、exf8R、exf8B。
A.レックス

1
1つの小さな問題-5ビットは32個の値しかエンコードしません。ボード上の任意の正方形を指定するには、6ビットが必要です。
Chris Dodd、

4

昨夜この質問を見て興味をそそられたので、解決策を考えてベッドに座った。私の最終的な答えは、実際にはint3とかなり似ています。

基本的な解決策

標準のチェスゲームでルールをエンコードしない場合(白が常に最初になる)、各ピースの動きだけをエンコードすることで大幅な節約が可能です。

合計32個のピース​​がありますが、各移動でどの色が移動しているかがわかるので、気になるのは16の正方形のみです。これは、このピースがこのターンを移動する4ビットです。

各ピースには限られたムーブセットしかありません。これは何らかの方法で列挙できます。

  • ポーン:4つのオプション、2ビット(前に1ステップ、前に2ステップ、各対角に1)
  • Rook:14オプション、4ビット(各方向で最大7)
  • ビショップ:13オプション、4ビット(1つの対角線に7つある場合、もう1つの対角線に6つしかない)
  • ナイト:8つのオプション、3ビット
  • クイーン:27オプション、5ビット(Rook + Bishop)
  • キング:9つのオプション、4ビット(8つのワンステップムーブとキャスリングオプション)

プロモーションでは、4つのピース(Rook、Bishop、Knight、Queen)から選択できるため、2ビットを追加してそれを指定します。他のすべてのルールは自動的にカバーされると思います(例:en enpassant)。

さらなる最適化

最初に、1つの色の8個がキャプチャされた後、ピースのエンコードを3ビットに減らし、次に4個の場合は2ビットに減らすことができます。

ただし、主な最適化は、ゲームの各ポイントで可能な動きのみを列挙することです。ポーンの動き{00, 01, 10, 11}をそれぞれ1ステップ前、2ステップ前、左斜め、右斜めに保存するとします。いくつかの移動が不可能な場合は、このターンのエンコーディングからそれらを削除できます。

私たちはあらゆる段階(すべての動きを追跡することから)でのゲームの状態を知っているので、どの部分が動くかを読んだ後、いつでも何ビット読む必要があるかを決定できます。この時点でポーンの唯一の動きが右斜めにキャプチャされているか、1つ前に進んでいることに気付いた場合、1ビットしか読み取らないことがわかります。

つまり、上記の各部分のビットストレージは最大値のみです。ほぼすべての移動で、オプションが少なくなり、多くの場合ビットが少なくなります。


4

各位置で可能なすべての動きの数を取得します。

次の移動は次のように生成されます

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

ランダムに生成されたゲームを保存するのに最適なスペース効率であり、30〜40の移動が可能なため、平均で約5ビット/移動が必要です。ストレージをアセンブルすると、nが逆の順序で生成されます。

優れた冗長性のため、保管位置をクラックするのは困難です。(1つのサイトで最大9つのクイーンがボード上に存在することができますが、その場合、ポーンはなく、ボード上の反対側の色の正方形にビショップはありません)が、一般的には、残りの正方形の上に同じピースの組み合わせを格納するようなものです。)

編集:

ムーブを保存するポイントは、ムーブのインデックスのみを保存することです。Kc1-c2を格納してこの情報を削減する代わりに、確定的なmovegenerator(position)から生成されたmoveのインデックスのみを追加する必要があります

各移動でサイズの情報を追加します

num_of_moves = get_number_of_possible_moves(postion) ;

プールでこの数を減らすことはできません

情報プールの生成は

n=n*num_of_moves+ index_current_move

余分な

最終位置で使用可能な移動が1つしかない場合は、以前に行われた強制移動の数として保存します。例:開始位置に各側の強制移動が1つ(2移動)あり、これを1つの移動ゲームとして保存する場合は、プールnに1を保存します。

情報プールに格納する例

既知の開始位置があり、3つの移動を行うとします。

最初の移動では5つの利用可能な移動があり、移動インデックス4を取得します。2番目の移動では6つの利用可能な移動があり、位置インデックス3を取得します。 2。

ベクトル形式; index = [4,3,2] n_moves = [5,6,7]

この情報を逆方向にエンコードしているため、n = 4 + 5 *(3 + 6 *(2))= 79(7を掛ける必要はありません)

これをループ解除するには?最初にポジションがあり、5つの手があることを確認します。そう

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

ムーブインデックス4を取り、位置をもう一度調べます。この時点から、6つの可能なムーブがあることがわかります。

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

そして、ムーブインデックス3を取得します。これにより、7つの可能な動きがある位​​置に移動します。

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

最後にインデックス2を移動し、最終位置に到達します。

ご覧のとおり、時間の複雑さはO(n)であり、空間の複雑さはO(n)です。編集:乗算する数が増えるため、時間の複雑さは実際にはO(n ^ 2)ですが、最大10,000の移動までゲームを保存しても問題はありません。


保存位置

最適に近い状態で行うことができます。

情報について知り、情報を保存するときに、それについて詳しく説明します。一般的な考え方は、冗長性を減らすことです(これについては後で説明します)。プロモーションもテイクも行われなかったと仮定して、ポーンが8つ、ルークが2つ、ナイトが2つ、ビショップが2つ、キングが1つ、クイーンが1つあるとします。

何を保存する必要がありますか:1.各平和の位置2.キャスリングの可能性3.エンパッサントの可能性4.移動可能な側

すべてのピースがどこにでも立つことができるが、同じ場所に2つはできないと仮定しましょう。ボード上で同じ色の8つのポーンを配置できる方法の数は、32ビットのC(64/8)(2項)であり、2つのルーク2R-> C(56/2)、2B-> C(54/2)です。 、2N-> C(52/2)、1Q-> C(50/1)、1K-> C(49/1)、他のサイトでも同じですが、8Pから始まります-> C(48/8)など。

両方のサイトでこれを掛け合わせると、約142ビットである番号4634726695587809641192045982323285670400000が得られます。1つの可能なen-passantに8を追加する必要があります(en-passantポーンは8か所のいずれかにある可能性があります)。移転したサイトは1ビット。最終的には、142 + 3 + 4 + 1 = 150ビットになります。

しかし、今度は32個のボードを使って冗長性を探し、テイクを取りません。

  1. 黒と白のポーンは同じ列にあり、向かい合っています。各ポーンは他のポーンに面しています。つまり、白いポーンは最大で6番目のランクにすることができます。これにより、情報が56ビット減少するC(64/8)* C(48/8)ではなく、8 * C(6/2)になります。

  2. キャスリングの可能性も冗長です。ルークが開始位置にない場合、そのルークがキャスリングする可能性はありません。したがって、このルークをキャスティングすることが可能であり、4つのキャスリングビットを削除する場合、追加の情報を取得するために、ボード上に4つの正方形を追加することを想像できます。したがって、C(56/2)* C(40/2)* 16の代わりに、C(58/2)* C(42/2)があり、3.76ビット(ほとんどすべての4ビット)を失っています。

  3. en-passant:8つのen passant possibiliteの1つを格納すると、黒のポーンの位置がわかり、情報の再配置が減少します(白の移動で3番目のポーンen-passantがある場合、黒のポーンがc5にあり、白のポーンがc2、c3またはc4)C(6/2)を使用しているため、3があり、2.3ビットが失われました。実行可能な側からも聖霊降臨祭の番号を保存すると(3つの可能性->左、右、両方)、冗長性が減少します。また、歩兵を乗せることができるポーンの位置がわかっています。(たとえば、c5の前のen enpassantの例は、左、右、またはその両方にある可能性のある黒です。1つのサイトにある場合、2 * 3になります(3つはプシシビライトを格納し、2つは7または6ランクの黒のポーンに移動できます) )C(6/2)を挿入し、1.3ビット削減し、両側で4.2ビット削減すると、2.3 + 1.3 = 3削減できます。

  4. ビショップ:bisopはopostiteの正方形にのみ配置できます。これにより、各サイトの冗長性が1ビット減少します。

まとめると、テイクがなかった場合、チェスの位置を格納するために 150-56-4-3.6-2 = 85ビットが必要です

そして、テイクとプロモーションが考慮に入れられている場合はおそらくそれほど多くはありません(しかし、誰かがこの長い投稿が役立つと思うなら、後でそれについて書きます)


興味深いアプローチ。さらに詳細を追加します:)
Andrew Rollings

ポジションを保存するためのアプローチも追加しました。私は取らない位置で85ビットに降りました、そして、それがどこまで行くことが可能であるかについての良い説明です。最良のアイデアは、ほぼすべてのオフ4ビットが冗長であるキャストリングの可能性を節約することだと思います。
Luka Rahne、

3

ほとんどの人はボードの状態をエンコードしていますが、動き自体については..ここにビットエンコードの説明があります。

1個あたりのビット数:

  • ピースID:片側あたり16個を識別するための最大4ビット。白/黒が推測できます。ピースに順序を定義します。ピースの数がそれぞれの2の累乗を下回るように、残りのピースを説明するために使用するビット数を減らします。
  • ポーン:最初の移動で3つの可能性があるため、+ 2ビット(1つまたは2つの四角で前進、全体)。その後の移動では、2つ前に移動できないため、+ 1ビットで十分です。ポーンが最後のランクに到達したタイミングを記録することで、デコードプロセスでプロモーションを推測できます。ポーンが昇格することがわかっている場合、デコーダーは4つの主要な部分のどれに昇格したかを示す別の2ビットを期待します。
  • ビショップ:対角線に使用する+1ビット、対角線に沿った距離に最大+4ビット(16の可能性)。デコーダーは、ピースがその対角線に沿って移動できる最大可能な距離を推測できるため、それがより短い対角線である場合は、使用するビットを減らします。
  • 騎士: 8つの可能な動き、+ 3ビット
  • ルーク:水平/垂直の場合は+1ビット、ラインに沿った距離の場合は+4ビット。
  • キング: 8可能な移動、+ 3ビット。キャスリングは「不可能な」動きで示します-キャスリングは王が最初のランクにいる間のみ可能であるため、この動きを王を「後ろに」動かす命令でエンコードします-つまり、ボードの外に。
  • クイーン: 8つの可能な方向、+ 3ビット。線/対角線に沿った距離が最大+4ビット増加(対角線が短い場合は、ビショップの場合のように少なくなります)

すべてのピースがボード上にあると仮定すると、これらは1移動あたりのビット数です。ポーン-最初の移動で6ビット、その後で5ビット。昇格した場合は7。ビショップ:9ビット(最大)、ナイト:7、ルーク:9、キング:7、クイーン:11(最大)。


ピースを識別するための32ビット??? 5個(32個)でしたね。または、「終了」状態をエンコードする必要がある場合は6
Toad

ポーンは、ルーク、ナイト、ビショップに昇格することもできます。これは、行き詰まりを避けるため、または対立を避けるために一般的です。
コビ

これはあなたの推論には影響しませんが、小さな修正です:ポーンはプロモーションを含まない4つの動き、またはプロモーションを含む12つの動きを持つことができます。e2のポーンの例:e3、e4、exd3、exf3。e7でのポーンの例:e8Q、e8N、e8R、e8B、exd8Q、exd8N、exd8R、exd8B、exf8Q、exf8N、exf8R、exf8B。
A.レックス

多分私は誤解しているかもしれませんが、ポーンはそれが最初の動きであることで一歩先を行くことができません。それはゲームのルールにあるので、実際には特別な "en passant"表記は必要ありません。それは対角線上の動きになるだけです。最初の移動は4つのオプションの1つで、その後の移動は最大3つのオプションです。
DisgruntledGoat

3

典型的なチェスのゲームで最も効率的なエンコーディングを与えるのが問題ですか、それとも最悪の場合のエンコーディングが最も短いエンコーディングですか?

後者の場合、最も効率的な方法は最も不透明でもあります。可能なすべてのペア(最初のボード、正当な動きのシーケンス)の列挙を作成します。 -fifty-movesは、last-pawn-move-or-captureルール以降、再帰的です。次に、この有限シーケンス内の位置のインデックスは、最悪の場合の最短のエンコーディングを提供しますが、典型的なケースの場合も同様に長いエンコーディングを提供し、計算には非常にコストがかかると思います。可能な限り最長のチェスゲームは5000手を超えるはずです。通常、各プレーヤーは各ポジションで20から30の手が利用できます(駒が少ない場合は少なくなります)。これにより、このエンコードに必要な40000ビットが得られます。

上記のヘンクホルターマンのエンコーディングの動きに関する提案で説明されているように、列挙のアイデアは、より扱いやすいソリューションを提供するために適用できます。私の提案:最小限ではありませんが、私が見た上記の例よりも短く、合理的に扱いやすいです:

  1. どの正方形が占有されているかを表す64ビット(占有マトリックス)、および各占有された正方形にあるピースのリスト(ポーン用に3ビット、その他のピース用に4ビットを持つことができます):これにより、開始位置に190ビットが与えられます。ボード上に32個を超えることはできないため、占有マトリックスのエンコードは冗長であり、共通のボード位置のようなものをエンコードできます(たとえば、33セットビット+共通ボードリストからのボードのインデックス)。

  2. 誰が最初に動くかを言うために1ビット

  3. ヘンクの提案によるコードの移動:プレーヤーが代替の移動を行わない場合、一部の移動は0ビットを消費しますが、通常は白/黒の移動のペアごとに10ビットです。

これは、典型的な30移動ゲームをコード化するために490ビットを示唆し、典型的なゲームの合理的な効率的な表現になります。

last-pawn-move-or-captureルール以降のdraw-on-thrice-repeated-positionとno-more-than-fifty-movesのエンコードについてこれらのルールが適用されるかどうかを決定するのに十分な情報を持っている:ゲーム履歴全体を必要としない。


たくさんのゲームを選び、結果の平均を取ると仮定します。
Andrew Rollings、

3

ボード上の位置は7ビットで定義できます(0〜63、およびボード上にないことを示す1つの値)。したがって、ボード上のすべてのピースについて、それが配置されている場所を指定します。

32ピース* 7ビット= 224ビット

編集:カドリアンが指摘したように...「ポー​​ンからクイーンへの昇格」ケースもあります。最後にビットを追加して、どのポーンが昇格したかを示します。

したがって、昇格されたすべてのポーンについて、224ビットの後に、昇格されたポーンのインデックスを示す5ビットと、リストの最後の場合は11111が続きます。

したがって、最小限のケース(プロモーションなし)は224ビット+ 5(プロモーションなし)です。昇格したポーンごとに5ビットを追加します。

編集:毛むくじゃらのカエルが指摘するように、それは誰の順番であるかを示すために最後にさらに別のビットが必要です; ^)


そして結果を後でgzip(ヘッダーが結果を増加させない場合); ^)
Toad

一部の作品が特定の正方形の色では見られないことを考慮に入れて、それを改善できますか?
Andrew Rollings、

andrew:実際にはできません。私は(カドリアンの答えが示唆するように)クイーンへの昇格されたポーンを考慮することを忘れていました。したがって、実際には別のビットがさらに必要になるようです
Toad

黒と白の司教を一緒に定義する方法を見ることができます。でも騎士については不思議に思います
。– int3

1
クイーン以外のプロモーションがありません。
Loren Pechtel、2009

2

ランレングスエンコーディングを使用します。いくつかの作品はユニーク(または2度しか存在しない)なので、長さは省略できます。クレタスと同様に、13個の一意の状態が必要なので、ニブル(4ビット)を使用してピースをエンコードできます。初期ボードは次のようになります。

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

これにより、8 + 2 + 4 + 2 + 8ニブル= 24ニブル= 96ビットが残ります。16をニブルでエンコードすることはできませんが、「空、0」は意味がないため、「0」を「16」として扱うことができます。

ボードが空であるが左上の1つのポーンの場合、「ポーン、1、空、16、空、16、空16、空、15」= 10ニブル= 40ビットになります。

最悪のケースは、各ピースの間に空の正方形がある場合です。しかし、ピースのエンコーディングには16個の値のうち13個が必要なので、別の値を使用して「Empty1」と言うことができます。次に、64ニブル== 128ビットが必要です。

ムーブメントについては、ピースに3ビット(白が常に最初に移動するという事実によって色が与えられます)に加えて、新しい位置に5ビット(0..63)=ムーブメントごとに1バイトが必要です。ほとんどの場合、範囲内にあるのは1つのピースだけなので、古い位置は必要ありません。奇妙なケースでは、未使用の単一のコードを使用し(ピースをエンコードするには7つのコードが必要です)、古い位置に5ビット、新しい位置に5ビットを使用する必要があります。

これにより、キャストを13バイトにエンコードできます(私はキングをルークに向かって動かすことができます。

[編集]スマートエンコーダーを許可する場合は、初期設定に0ビット(静的にエンコードする必要がないため、静的である)に加えて、移動ごとに1バイト必要です。

[編集2]ポーン変換を残します。ポーンが最後の行に到達した場合、それを所定の位置に移動して「変形」と言ってから、置き換える部分に3ビットを追加できます(クイーンを使用する必要はありません。ポーンは何でも置き換えることができます。しかし王)。


スマートエンコーダーは、それがゲーム全体であるとは想定できません。ゲームの一部である可能性があります。まだ開始位置をエンコードする必要があると思います。
Andrew Rollings、

最悪の場合、128ビットが必要です。ゲームがまだ初期段階にある場合は、最大15移動で開始位置= 120ビットにすることができます。
アーロン・ディグラ2009

ボードの初期状態だけでなく、すべての状態をエンコードする必要があるため、ピース自体もエンコードする必要があります。したがって、1個あたり少なくとも5ビットが必要です。したがって、これにより少なくとも32 * 5ビットが追加されます
Toad

@ライナー:あなたは間違っている。ピース/空の正方形あたり4ビットが必要です。そして、私は私の答えの最初の部分でこれをすでにカバーしたので、「32 * 5ビット余分」はありません。初期状態では96ビットが必要で、その他の開始状態では最大128ビットが必要です。
アーロンディグラ2009

アーロン:あなたが言うように、最悪のシナリオはこのエンコーディングで本当に最悪のケースです。スタートボードから3または4移動した後、空のビットを追加していくため、エンコードにはかなり多くのビットが必要になります
Toad

2

本や紙にゲームをエンコードするのと同じように、すべての作品にシンボルがあります。「合法」ゲームなので、白が最初に動きます。白または黒を個別にエンコードする必要はありません。動きの数を数えて、誰が動いたかを判断します。また、すべての移動は(ピース、終了位置)としてエンコードされます。「終了位置」は、あいまいさを識別することができる(ゼロの場合もある)シンボルの最小量に削減されます。ゲームの長さが移動回数を決定します。すべてのステップで(最後の移動以降の)時間を分単位でエンコードすることもできます。

ピースのエンコードは、シンボルをそれぞれ(合計32)に割り当てるか、クラスにシンボルを割り当て、終了位置を使用して、どのピースが移動されたかを理解することによって実行できます。たとえば、ポーンには6つの可能な終了位置があります。しかし、平均して、毎ターン、カップルしか利用できません。したがって、統計的には、終了シナリオによるエンコードがこのシナリオに最適です。

同様のエンコーディングは、計算神経科学(AER)のスパイクトレインに使用されます。

欠点:リンクリストをたどるように、現在の状態に到達してサブセットを生成するには、ゲーム全体をリプレイする必要があります。


それはゲームの断片に過ぎないかもしれません。あなたは白の動きが最初であると仮定することはできません。
Andrew Rollings、

2

64の可能なボード位置があるので、位置ごとに6ビットが必要です。32個の初期ピースがあるため、これまでのところ合計192ビットであり、6ビットごとに特定のピースの位置を示しています。ピースが表示される順序を事前に決定できるので、どちらがどちらであるかを言う必要はありません。

ピースがボードから外れている場合はどうなりますか?まあ、そうでなければ違法になるので、ボードから外れていることを示すために、別のピースと同じ場所にピースを置くことができます。しかし、最初のピースがボードに載るかどうかもわかりません。したがって、最初のピースを示す5ビットを追加します(32の可能性=最初のピースを表す5ビット)。次に、そのスポットを使用して、ボードから外れた後続のピースを作成します。これにより、合計197ビットになります。ボード上に少なくとも1つのピースがなければならないので、それはうまくいきます。

次に、その順番が1ビット必要です-198ビットになります

ポーンプロモーションについてはどうですか?ポーンごとに3ビットを追加し、42ビットを追加することで、悪い方法を実行できます。しかし、ほとんどの場合、ポーンは昇格されないことがわかります。

したがって、ボード上にあるすべてのポーンについて、ビット「0」はそれが昇格されていないことを示します。ポーンがボード上にない場合、少しも必要ありません。次に、彼が昇格した可変長ビット文字列を使用できます。ほとんどの場合はクイーンになります。したがって、「10」はクイーンを意味します。その場合、「110」はルーク、「1110」はビショップ、「1111」はナイトを意味します。

16個のポーンがすべてボード上にあり、昇格されていないため、初期状態は198 + 16 = 214ビットになります。2つのプロモーテッドポーンクイーンを持つエンドゲームでは、198 + 4 + 4のようになります。これは、4つのポーンが生きていてプロモートされていないことと、2つのクイーンポーンが合計206ビットであることを意味します。かなり頑丈なようです!

===

他の人が指摘したように、ハフマンエンコーディングは次のステップになるでしょう。数百万のゲームを観察すると、各ピースが特定の正方形に配置される可能性がはるかに高いことがわかります。たとえば、ほとんどの場合、ポーンは直線または1つ左/ 1つ右に留まります。王は通常、本拠地を固執します。

したがって、個々の位置ごとにハフマン符号化スキームを考案してください。ポーンは、おそらく6ではなく、平均3〜4ビットしかかかりません。キングも数ビットを取る必要があります。

また、このスキームでは、可能な位置として「取られた」を含めます。これはキャスリングも非常に堅牢に処理できます。ルークとキングはそれぞれ「元の位置、移動」状態になります。この方法でポーンのen passantをエンコードすることもできます-「元の位置、can en passant」。

十分なデータがあれば、このアプローチは本当に良い結果をもたらすはずです。


2
取り除いた駒を王と同じ正方形に割り当てるだけです。王は決して取り除けないので、それは曖昧ではないでしょう
ジョン・ラ・ロイ2009

それは良いコメントです:)この解決策にも良い面があります。勝者を選ぶのがそれほど難しいとは思いもしませんでした。
Andrew Rollings

2

私はハフマンエンコーディングを使おうと思います。これの背後にある理論は-すべてのチェスゲームで、多くの動きをするいくつかの駒と、あまり動かないか、早く排除される駒があるでしょう。開始位置にすでにいくつかのピースが削除されている場合-さらに良いです。同じことが正方形にも当てはまります-いくつかの正方形はすべてのアクションを見ることができますが、あまり触れられない正方形もあります。

したがって、私には2つのハフマンテーブルがあります。1つはピース用、もう1つは正方形用です。実際のゲームを見ることで生成されます。ピースとスクエアのペアごとに1つの大きなテーブルを作成することもできますが、同じスクエアで同じピースが再び移動するインスタンスが多くないため、これはかなり非効率的だと思います。

すべてのピースにIDが割り当てられます。32個の異なるピースがあるので、ピースIDに必要なのは5ビットだけです。ピースIDはゲームごとに変わりません。同じことが正方形のIDにも当てはまり、6ビットが必要になります。

ハフマンツリーは、各ノードが順番にトラバースされるときに各ノードを書き留めることでエンコードされます(つまり、最初にノードが出力され、次にその子が左から右へ)。すべてのノードについて、それがリーフノードであるかブランチノードであるかを1ビット指定します。リーフノードの場合は、IDを示すビットが後に続きます。

開始位置は、一連のピースとロケーションのペアによって単純に与えられます。その後、移動ごとにピースとロケーションのペアが1つあります。2回言及されている最初の部分を見つけるだけで、開始位置記述子の終わり(および移動記述子の始まり)を見つけることができます。ポーンが昇格した場合、ポーンがどのようになるかを指定する2ビットが追加されますが、ピースIDは変更されません。

ゲームの開始時にポーンが昇格される可能性を考慮するために、ハフマンツリーとデータの間に「昇格テーブル」も存在します。最初は、アップグレードするポーンの数を指定する4ビットがあります。次に、ポーンごとに、そのハフマンエンコードされたIDと、何になったかを指定する2ビットがあります。

ハフマンツリーは、すべてのデータ(開始位置と移動の両方)とプロモーションテーブルを考慮して生成されます。通常、プロモーションテーブルは空か、数個のエントリしかありません。

グラフでまとめると、次のようになります。

<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>)

追加:これはまだ最適化できます。すべての作品は、いくつかの法的な動きのみを持っています。ターゲットの正方形を単純にエンコードする代わりに、すべてのピースの可能な動きに0ベースのIDを与えることができます。すべてのピースで同じIDが再利用されるため、合計で21を超える異なるIDは存在しません(クイーンは最大21の異なる移動オプションを持つことができます)。これをフィールドの代わりにハフマンテーブルに入れます。

ただし、これでは元の状態を表すのが困難になります。各ピースをその場所に置くために、一連の動きを生成する場合があります。この場合、何とかして初期状態の終了と移動の開始をマークする必要があります。

または、非圧縮の6ビットの正方形IDを使用して配置することもできます。

これが全体的なサイズの減少をもたらすかどうか-私は知りません。おそらく、少し実験する必要があります。

追加2:もう1つの特別なケース。ゲームの状態に移動がない場合、次に移動する人を区別することが重要になります。そのため、最後にもう1ビット追加します。:)


2

[質問を適切に読んだ後に編集]すべてのリーガルポジションが最初のポジション( "リーガル"の可能な定義)から到達できると想定すると、どのポジションも最初からの移動のシーケンスとして表すことができます。非標準の位置から始まるプレイのスニペットは、開始に到達するために必要な一連の動き、カメラをオンにするスイッチ、その後の動きが続くものとして表現できます。

したがって、ボードの初期状態を単一ビット「0」と呼びましょう。

任意の位置からの移動は、正方形に番号を付け、移動を(開始、終了)の順に並べることによって列挙できます。従来の2つの正方形のジャンプは、キャスティングを示します。ボードの位置とルールは常に既知であるため、違法な動きをエンコードする必要はありません。カメラをオンにするフラグは、特別なインバンドムーブとして表現することも、バンド外ムーブ番号としてよりわかりやすく表現することもできます。

両側に24のオープニングムーブがあり、それぞれ5ビットに収まります。後続の移動には多少のビットが必要になる場合がありますが、合法的な移動は常に列挙可能であるため、各移動の幅は喜んで拡大または拡張できます。計算はしていませんが、7ビットの位置はまれだと思います。

このシステムを使用すると、100のハーフムーブゲームを約500ビットでエンコードできます。ただし、オープニングブックを使用することをお勧めします。100万のシーケンスが含まれているとします。次に、最初の0は標準ボードからの開始を示し、1の後に20ビット数が続くと、そのオープニングシーケンスからの開始を示します。やや従来型のオープニングのゲームは、たとえば20ハーフムーブ、つまり100ビット短縮される可能性があります。

これは可能な限り最大の圧縮ではありませんが、(オープニングブックなしで)質問が仮定しているチェスモデルが既にある場合は、非常に簡単に実装できます。

さらに圧縮するには、任意の順序ではなく、可能性に従って移動を順序付け、可能性のあるシーケンスをより少ないビットでエンコードします(たとえば、人々が述べたようにハフマントークンを使用します)。


最初の位置は必ずしもわかっていません。ゲームの断片かもしれません。
Andrew Rollings、

@アンドリュー:はい。私の間違い。ゲームのフラグメントを許可するように編集しました。
ダグラスバニャール

2

計算時間が問題にならない場合は、確定的な可能な位置ジェネレータを使用して、特定の位置に一意のIDを割り当てることができます。

所定の位置から、まず確定的な方法で可能な位置の数を生成します。たとえば、左下から右上に移動します。これにより、次の移動に必要なビット数が決まります。状況によっては、1ビットでもかまいません。次に、移動が行われたときに、その移動の一意のIDのみを保存します。

昇格やその他のルールは、クイーン、ルーク、ビショップなどの決定論的な方法で処理される限り、単純に有効な移動としてカウントされ、各カウントは個別の移動としてカウントされます。

最初の位置は最も難しく、約2億5,000万の可能な位置を生成する可能性があります(私は思う)、それはだれの移動かを決定するために約28ビットと追加のビットを必要とするでしょう。

それが誰であるかを知っていると仮定すると(各ターンが白から黒に反転する)、決定論的ジェネレータは次のようになります。

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

「可能な動きのリストを取得する」は次のようになります:

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.

キングがチェックされている場合、各「可能なxxxムーブのリスト」は、チェックの状況を変える有効なムーブのみを返します。


これは卑劣なソリューションです...だから...この例では、決定論的数を生成するためのアルゴリズムを説明します。
Andrew Rollings

チェス盤ですべてのポジションを生成するのにかかる時間に関する興味深いリンクを見つけました:) ioannis.virtualcomposer2000.com/math/EveryChess.html
Andrew Rollings

2

ほとんどの回答は3倍の繰り返しを見落としていた。残念ながら3倍の繰り返しでは、これまでにプレイしたすべてのポジションを保存する必要があります...

この質問では、スペース効率の良い方法で保存する必要がありました。そのため、移動のリストから構築できる限り、位置を保存する必要はありません(標準の開始位置がある場合)。PGNを最適化でき、それで終わりです。怒鳴るは単純なスキームです。

ボードには64の正方形があり、64 = 2 ^ 6です。12ビットを必要とする各移動の最初と最後の正方形のみを保存する場合(プロモーションは後で取り組みます)。このスキームは、プレイヤーの移動、強調、駒のキャプチャ、キャスティングなどをすでにカバーしています。現時点では、ムーブリストを再生するだけで作成できます。

昇格のために、「移動NでピースXYZに昇格する」というベクトルの個別の配列を保持できます。(int、byte)のベクトルを保持できます。

(To、From)ベクトルも最適化するのは魅力的です。これらの(To、From)ベクトルの多くはチェスでは不可能だからです。例えば。e1からd8などへの移行はありません。しかし、私はどんなスキームも思いつきませんでした。それ以上のアイデアは大歓迎です。


2

私はそれについて長い間考えました(+-2時間)。そして明白な答えはありません。

仮定:

  1. 時間の状態を無視する(プレイヤーが時間制限を使用しなかったため、プレイしないことでドローを強制することができた)
  2. ゲームはいつプレイされましたか?!?ルールは時間の経過とともに変化するため、これは重要です(次の時点で最新のゲームが最新のゲームであると想定します...)。たとえば、デッドポーンルールを参照してください(ウィキペディアにそれを示す非常に有名な問題があります)。時間をさかのぼるには、幸運の司教はゆっくりと動いて、サイコロが使われていた。笑。

...つまり、最新の最新のルールです。最初は繰り返しに関係なく、繰り返しの限界を移動します。

-C丸められた25バイト(64b + 32 * 4b + 5b = 325b)

= 64ビット(何か/何もない)+ 32 * 4ビット[1bit = color {black / withe} + 3bit = piece of type {King、Queen、Bishop、kNight、Rook、Pawn、MovedPawn} NB:移動したポーン...例えば、それが前のターンで最後に動かされたポーンであった場合、「en passant」が実行可能であることを示します。]実際の状態の+5ビット(誰のターンか、通行人か、両サイドでルークできるかどうか)

ここまでは順調ですね。おそらく拡張することができますが、その場合、考慮すべき可変長とプロモーションがあります!?

さて、以下のルールは、プレイヤーがドローのためにプレーするときにのみ適用され、それは自動ではありません!ですから、この90の動きはキャプチャなしで考えてください。プレイヤーがドローを要求しなければ、ポーンの動きは実現可能です。すべての動きが記録され、利用可能である必要があることを意味します。

-D位置の繰り返し...たとえば、上記のボードの状態(Cを参照)かどうか(FIDEルールについては以下を参照)-Eこれにより、キャプチャまたはポーンの移動なしに50の移動手当という複雑な問題が残されます。カウンターが必要ですが…。

それで、どうやってそれに対処しますか?...まあ、本当に方法はありません。どちらのプレイヤーもドローしたくない、またはそれが発生したことに気付かないからです。さて、Eの場合はカウンターで十分かもしれません...しかし、ここにトリックとFIDEルール(http://www.fide.com/component/handbook/?id=124&view=article)があります。答え...ルーキングの能力の喪失についてはどうですか?それは繰り返しですか?私はそうではないと思いますが、これはぼやけた主題であり、取り組まれておらず、明確化されていません。

だからここに2つのルールがあります。これは、エンコードしようとしても2つ複雑または未定義です...乾杯。

したがって、ゲームを真にエンコードする唯一の方法は、最初からすべてを記録することです...これは、「ボード状態」の質問と競合します(またはそうではありませんか?)。

このヘルプが...数学が多すぎないことを願っています:-)いくつかの質問がそれほど簡単ではなく、解釈または事前知識が正しく効率的であるには余りにも開かれていないことを示します。あまりにも多くのワームの缶を開けてしまうので、インタビューの対象として検討する人はいません。


2

Yacobyのソリューションにおける開始位置の改善の可能性

各色が16個を超える法的地位はありません。64個の正方形に最大16個の黒と16個の白のピースを配置する方法の数は、約3.63e27です。Log2(3.63e27)= 91.55。つまり、すべてのピースの位置と色を92ビットでエンコードできます。これは、位置の64ビットより少なく、Yacobyのソリューションが必要とするカラーの32ビットまでです。エンコーディングがかなり複雑になる代わりに、最悪の場合は4ビットを節約できます。

一方、5つ以上のピースが欠落しているポジションのサイズは増加します。これらの位置はすべての位置の4%未満にすぎませんが、最初の位置とは異なる開始位置を記録したい場合の大部分はおそらくです。

これは完全なソリューションにつながります

  1. 上記の方法に従って、ピースの位置と色をエンコードします。 92ビット
  2. 各ピースのタイプを指定するには、ハフマンコードを使用します:ポーン: '0'、ルーク: '100'、ナイト: '101'、ビショップ: '110'、クイーン: '1110'、キング: '1111'。これには、ピースの完全なセットに対して(16 * 1 + 12 * 3 + 4 * 4)= 68ビットが必要です。ボード全体の位置は、最大 92 + 68 = 160ビットでエンコードできます。
  3. 追加のゲーム状態を追加する必要があります:ターン:1ビット、キャスティングが可能:4ビット、「不変」可能:最大4ビット(1ビットがそれを示し、3ビットがどちらを示すか)。開始位置は= 160 + 9 = 169ビットでエンコードされます
  4. ムーブのリストについては、特定の位置で可能なすべてのムーブを列挙し、そのムーブの位置をリストに保存します。移動のリストには、すべての特殊なケース(キャスティング、エンパッサント、および辞任)が含まれています。最高位を格納するために必要なビットだけを使用してください。平均して、1ムーブあたり7ビットを超えてはなりません(平均で16ピースと1ピースあたり8リーガルムーブ)。場合によっては、移動が強制されたときに、1ビットのみが必要です(移動または辞任)。

1

ボードには32個の部品があります。各ピースには位置があります(64マスに1つ)。したがって、必要なのは32の正の整数だけです。

私は64ビットが6ビットで保持されることを知っていますが、そうはしません。いくつかのフラグ(ドロップされたピース、クイーンのポーン)の最後のビットを保持します


状態を保持するためにフラグを使用する必要はありません。エンコーダーは「ルールを理解する」のに十分スマートであると想定できます。したがって、ポーンが突然クイーンに変わった場合、必ずしもエンコードで特にフラグを立てる必要はありません(ただし、プレイヤーがプロモートしないことを選択した場合を除きます)。
Andrew Rollings、

ポーンが昇格したかどうかはポーンの最初の位置ではわからないので、そうするべきです!初期設定でエンコードされるように
Toad

でも、なぜそれがすでに宣伝されているのかを知る必要があるのですか?ほんの一部です。この場合、過去の状態は関係ありません。
Andrew Rollings、

ポーンがまだポーンであるか、クイーンに昇格されているかどうかは、ゲームの残りの部分にはほとんど関係がないと思います。あなたがそう思わないなら、私はあなたとチェスのゲームをプレイしたいです; ^)
Toad

@reinier:彼は、現在の女王がもともと女王だったのかポーンだったのかは関係ないと主張している。
A.レックス

1

cletusの答えは良いですが、彼はそれが誰であるかをエンコードするのも忘れていました これは現在の状態の一部であり、その状態を使用して検索アルゴリズム(アルファ-ベータ導関数など)を駆動する場合に必要です。

私はチェスプレーヤーではありませんが、もう1つのコーナーケースがあると思います。各プレーヤーが同じ動きを3回行うと、ゲームは引き分けになります。もしそうなら、その情報を状態に保存する必要があります。これは、3回目の繰り返しの後、状態が最終状態になるためです。


実際のチェスゲームでは、両方のプレーヤーが合計で1時間または2時間しか考えないため、そのルートに進むには、両方のプレーヤーの再生時間も追加する必要があります。
ヒキガエル

2
実際のデータにルールをエンコードする必要はありません。エンコーダ自体が必要なルールを知っていると想定できます。
Andrew Rollings、

ああ..遊びの時間は考えていませんでした。グッドコール... :)
Andrew Rollings

@Andrew Rollings:ルールは状態ベースで、特定の前提条件が満たされた場合にのみトリガーされます。その前提条件の状態を追跡することも、...状態の一部です。:)
Shaggy Frog、

この場合は関係ありません。必要に応じて、デコーダは状態を調べて勝者を決定できます。エンコーダー/デコーダーはルールに対応しています。実際にエンコードする必要があるのはプレーヤーの選択だけです。それ以外は、エンコーダー/デコーダーが認識していると見なすことができます。
Andrew Rollings、

1

他のいくつかがあなたが32個のそれぞれについてあなたが彼らがいる正方形を保存することができると述べたように、それらがボード上にあるかどうか、これは32 *(log2(64)+ 1)= 224ビットを与えます。

ただし、司教は黒または白の正方形しか占有できないため、これらの場合、位置にlog2(32)ビットのみが必要であり、28 * 7 + 4 * 6 = 220ビットになります。

そして、ポーンは後ろから始まらず、前進することしかできないため、56にしか置くことができないため、この制限を使用して、ポーンに必要なビット数を減らすことができるはずです。


ビショップもボードから外れる可能性があるので、それらのために追加のビットが必要です。また、あなたは昇進したポーンと最初に始める人について忘れています。これらすべてを考慮に入れると、基本的には私の答えに終わります; ^)
Toad

ビショップの6ビットはlog2(32)+ 1 = 6ですが、すべての詳細を考慮すると、これは確かに複雑な質問です:)
Andreas Brinck

私はこれらの線に沿って考えていましたが、それは助けにはなりません。Thomasの回答を見て、ハフマンエンコーディングを変更して、空白の概念を削除します。64ビットを使用して、正方形が占有されているマトリックスを格納し、各エンコードから1ビットを削除します。これにより、同じ64ビットを正確に復元します。
Loren Pechtel、2009

1

ボードには64の正方形があり、正方形が空かどうかを示す64ビットで表すことができます。正方形に駒がある場合のみ駒情報が必要です。プレーヤー+ピースは4ビットを取るため(前に示したように)、64 + 4 * 32 = 192ビットで現在の状態を取得できます。現在のターンで投げると、193ビットです。

ただし、各ピースの法的な動きもエンコードする必要があります。最初に、各ピースの正当な移動の数を計算し、完全な正方形のピース識別子の後にその数のビットを追加します。私は次のように計算しました:

ポーン:フォワード、最初に2ターン、前向き* 2、プロモーション= 7ビット。最初のターンフォワードとプロモーションを同じ位置から発生させることはできないため、1ビットに組み合わせることができます。これにより、6になります。 * 7 = 14ビットのクイーン:垂直7、水平7、対角7、対角7 = 28ビットキング:周囲の8つの正方形

これは、現在の位置に基づいてターゲットの四角形をマップする必要があることを意味しますが、それは(あるべきです)単純な計算です。

16のポーン、4つのルーク/ナイト/ビショップ、2つのクイーン/キングがあるので、これは16 * 6 + 4 * 14 + 4 * 8 + 4 * 14 + 2 * 28 + 2 * 8 = 312ビット増え、合計で505ビットになります。

可能な移動のために必要なビット数については、いくつかの最適化をそれに追加でき、おそらくビット数が削減されたので、簡単な数値を使用して作業しました。たとえば、スライドピースの場合、どれだけ遠くまで移動できるかを保存できますが、これには追加の計算が必要になります。

簡単な説明:正方形が占有されている場合は追加のデータ(ピースなど)のみを保存し、合法的な動きを表すために各ピースの最小ビット数のみを保存します。

EDIT1:キャスティングとポーンの昇格について忘れました。これにより、明示的な位置を含む合計が557の移動になります(ポーン用に3ビット、キング用に2ビット)


1

各ピースは4ビット(ポーンからキング、6タイプ)、黒/白= 12値で表すことができます

ボード上の各正方形は、6ビット(x座標、y座標)で表すことができます。

初期位置には最大320ビットが必要です(32個、4 + 6ビット)

後続の各移動は、16ビット(from-position、to-position、piece)で表すことができます。

キャスリングは二重の動きなので、追加の16ビットが必要になります。

クィーンドポーンは、4ビットのうちの4つのスペア値の1つで表すことができます。

詳細な計算を行わずに、32 * 7ビット(事前定義されたピースの配列)または64 * 4ビット(事前定義された正方形の割り当て)を保存する場合と比較して、最初の移動後にスペースを節約し始めます。

両側で10回移動した後、必要な最大スペースは640ビットです

...しかし、繰り返しになりますが、各ピースを一意に識別し(5ビット)、クィーンポーンにフラグを付けるために6番目のビットを追加すると、各移動で必要なのはpiece-id + to-positionだけです。これは計算を次のように変更します...

初期位置=最大384ビット(32個、6 + 6ビット)各移動= 12ビット(位置、ピースID)

次に、10移動した後、必要な最大スペースは624ビットです。


2番目のオプションには、ストレージを12ビットのレコードとして読み取ることができるという追加の利点があります。各レコードは位置とピースです。最初の移動キャブは、ピースに以前のエントリがあるという事実によって検出されます。
Steve De Caux、

移動間の時間については、各レコードにカウンターのxビットを追加します。設定レコードの場合、これは0に設定されます
スティーブ・デ・コー

これは私が書き出そうとしていたアプローチです。最適化の1つは、標準的なゲームの場合、初期位置をまったくエンコードする必要がないことです。「これは標準的なゲームです」と言った先頭の1ビットで十分です。また、キャスティングは二重の移動を行いません-キャスリングの移動は常に明白であり、キャスリングの特定のキングの半分が発生したときにルークが移動する有効な方法は1つしかないため、冗長な情報です。プロモーションでは、ポーンが最後の行に移動した後の4ビットを使用して、新しいピースタイプを指定できます。
共龍2009

したがって、通常のゲームの場合、10ムーブした後、プロモーションがないと仮定すると、121ビットになります。非定型ゲームでは、フラグに1ビット、ピース* 10ビット、および最初のプレーヤーを示す別のビットが必要です。また、特定の正方形上の駒はゲーム内の前の動きから暗黙的に示されるため、各動きには12ビットしか必要ありません。これはおそらく提案された方法のいくつかよりも効率的ではありませんが、かなりクリーンで、「標準」のゲームにはかなり効率的です。
共龍2009

@kyoro-移動ごとに12ビットを打つことができると私は確信していません-標準セットアップにnullのアイデアを使用します(私は12ビットをビンゼロに設定して使用します)-両側で1000移動した後、これは別名24012ビットです3002バイト(切り上げ)何らかの形式の圧縮を使用したとしても、ハードコードされた(または論理的に導出可能な同じもの)辞書を宣言することにより、これを克服するためにチートする必要があります
Steve De Caux

1

Robert Gと同様に、PGNは標準であり、さまざまなツールで使用できるため、使用する傾向があります。

ただし、遠方の宇宙探査機でチェスAIをプレイしていて、すべてのビットが貴重である場合、これは私が移動のために行うことです。後で初期状態をエンコードするためにブロックします。

動きは状態を記録する必要はありません。デコーダーは、状態を追跡することができます。また、任意の時点で有効な動作を追跡できます。記録する必要があるすべての動きは、さまざまな法的代替案のどれが選択されるかです。選手は交代するので、移動は選手の色を記録する必要はありません。プレーヤーは自分のカラーピースのみを移動できるため、最初の選択肢はプレーヤーが移動するピースです(後で別の選択肢を使用する別の方法に戻ります)。最大16個で、これは最大4ビットを必要とします。プレーヤーが駒を失うと、選択肢の数が減ります。また、特定のゲーム状態により、駒の選択が制限される場合があります。王が自分自身を抑制せずに移動できない場合、選択肢の数は1つ減ります。キングがチェックされている場合、キングをチェックから外すことができないピースは実行可能な選択ではありません。

作品が指定されると、一定数の正当な目的地のみが含まれます。正確な数はボードレイアウトとゲーム履歴に大きく依存しますが、特定の最大値と期待値を把握できます。騎士以外のすべてとキャスリング中は、駒は別の駒を通過できません。これは移動制限の大きな原因になりますが、定量化するのは困難です。駒がボードから外れることができないため、目的地の数も大幅に制限されます。

ほとんどのピースの宛先を、W、NW、N、NE(黒い側はN)の順序で線に沿って正方形に番号を付けることによってエンコードします。線は、指定された方向の最も遠い正方形から始まり、移動先に向かって合法です。障害のない王の場合、移動のリストはW、E、NW、SE、N、S、NE、SWです。騎士の場合、2W1Nから始めて時計回りに進みます。宛先0は、この順序で最初の有効な宛先です。

  • ポーン:動かないポーンには2つの宛先の選択肢があるため、1ビットが必要です。ポーンが通常またはエンパッサン(状態を追跡しているためデコーダーが判別できる)を別のポーンがキャプチャできる場合、2つまたは3つの移動の選択肢もあります。それ以外は、ポーンには選択肢が1つしかなく、ビットは必要ありません。ポーンが7 番目のランクにある場合、プロモーションの選択にも取り組みます。通常、ポーンはクイーンに昇格し、次にナイトが昇格するため、選択肢を次のようにエンコードします。
    • クイーン:0
    • 騎士:10
    • ビショップ:110
    • ルーク:111
  • ビショップ:4ビットで{d、e} {4,5}の場合、最大13の宛先。
  • ルーク:最大14の宛先、4ビット。
  • ナイト:最大8つの宛先、3ビット。
  • キング:キャスティングがオプションの場合、キングはSに戻り、下に移動できません。これにより、合計7つの宛先が得られます。残りの時間では、キングは最大で8手を持ち、最大3ビットを与えます。
  • クイーン:ビショップまたはルークの選択肢と同じ、合計27ビット、つまり5ビット

選択肢の数は常に2の累乗であるとは限らないため、上記では依然としてビットを無駄にしています。選択肢の数がCで、特定の選択肢の番号がcであり、n = ceil(lg(C))(選択肢をエンコードするために必要なビット数)であるとします。次の選択肢の最初のビットを調べることにより、これらのその他の無駄な値を利用します。0の場合は何もしません。1でc + C <2 nの場合Ccに追加します。番号をデコードすること、これを逆転:受信した場合、C > = C、減算Cとした場合1に、次の番号の最初のビットをセットC<2 N - C、次いで2場合は0に、次の番号の最初のビットをセットN - C <= C < Cは、その後、何もしません。このスキームを「飽和」と呼びます。

エンコーディングを短くする可能性のある別の潜在的な選択のタイプは、キャプチャする対戦相手の駒を選択することです。これにより、移動の最初の部分の選択肢の数が増え、ピースが1つだけ増えます(正確な数は、現在のプレーヤーが移動できるピースの数によって異なります)。この選択の後に、キャプチャーピースの選択が続きます。これは、特定のプレーヤーのピースの移動数よりもはるかに少ない可能性があります。ピースは、任意のカーディナル方向からの1つのピースと騎士の合計で最大10の攻撃ピースでのみ攻撃できます。これにより、キャプチャ移動の合計最大9ビットが得られますが、平均で7ビットを期待します。多くの場合、法的な目的地が非常に多いため、これは女王による捕獲にとって特に有利です。

飽和状態では、キャプチャエンコーディングはおそらくメリットがありません。使用する初期状態を指定して、両方のオプションを許可できます。飽和が使用されない場合、ゲームのエンコーディングは未使用の選択肢番号(C <= c <2 n)を使用してゲーム中にオプションを変更することもできます。Cが2の累乗であるときはいつでも、オプションを変更できませんでした。


1

トーマスはボードをエンコードするための正しいアプローチを持っています。ただし、これは移動を保存するためのraluのアプローチと組み合わせる必要があります。可能なすべての動きのリストを作成し、この数を表現するために必要なビット数を書き出します。デコーダーは同じ計算を行っているため、可能な数がわかっていて、読み取るビット数がわかるため、長さコードは必要ありません。

したがって、ピースには164ビット、キャスリング情報には4ビット(ゲームのフラグメントを保存していると仮定します。それ以外の場合は再構築できます)、パスの資格情報には3ビットを取得します。移動が発生した列を保存するだけです( en passantが不可能な場合は、不可能な場所に列を格納します(そのような列が存在している必要があります)。

通常、移動には5ビットまたは6ビットかかりますが、1から8まで変化する可能性があります。

もう1つのショートカット-エンコードが12 1ビットで始まる場合(無効な状況-片側にも片側に2つのキングがない場合)、デコードを中止し、ボードをワイプして新しいゲームをセットアップします。次のビットは移動ビットになります。


1

アルゴリズムは、各移動で可能なすべての宛先を決定論的に列挙する必要があります。目的地の数:

  • ビショップ2人、目的地13個= 26
  • 2つのルーク、それぞれ14の宛先= 28
  • 2騎士、8目的地= 16
  • 女王、27の目的地
  • キング、8つの目的地

最悪の場合(列挙的)に8つの足がすべてクイーンになる可能性があるため、可能な宛先の最大数は9 * 27 + 26 + 28 + 16 + 8 = 321になります。したがって、移動のすべての宛先は、9ビットの数値で列挙できます。

両当事者の最大手数は100です(私が間違っていなければ、チェスプレイヤーではありません)。したがって、どのゲームも900ビットで記録できます。加えて、各ピースは、合計で32 * 6 = 192ビットになる6ビットの数値を使用して記録できます。さらに、「誰が最初に動くか」の記録のために1ビット。したがって、900 + 192 + 1 = 1093ビットを使用してゲームを記録できます。


1

ボードの状態を保存する

私が考えた最も単純な方法は、各ピースの位置を表す8 * 8ビットの配列を最初に持つことです(つまり、チェスの駒がある場合は1、ない場合は0)。これは固定長なので、ターミネーターは必要ありません。

次に、すべてのチェスの駒をその場所の順に表します。1個あたり4ビットを使用すると、32 * 4ビット(合計128)になります。これは本当に無駄です。

バイナリツリーを使用すると、ポーンを1バイトで表し、ナイトとルークとビショップを3で、キングとクイーンを4で表すことができます。 (これが間違っている場合は許してください、これまでハフマンコーディングを詳細に調べたことはありません):

  • ポーン:2
  • ルーク:4
  • ナイト:4
  • ビショップ:4
  • キング:5
  • クイーン:5

合計を考えると:

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

固定サイズのビットセットを使用して28ビットずつビートします。

だから私が見つけた最良の方法は、それを8 2 + 100ビット配列に格納することです

8*8 + 100 = 164



動き
保存最初に知っておくべきことは、どの部分がどこに移動するかです。ボード上に最大32個のピース​​があり、正方形を表す整数ではなく、各ピースが何であるかがわかっている場合、ピースのオフセットを表す整数を持つことができます。つまり、32個の可能な値をフィットするだけで、ピース。

残念なことに、王を投げたり倒したり、共和国を結んだりするなど、さまざまな特別なルールがあります(Terry Pratchettの参照)。そのため、駒を移動して保管する前に、それが特別な移動かどうかを示す1ビットが必要です。

したがって、通常の移動ごとに必要な1 + 5 = 6ビットがあります。(1ビットタイプ、駒は5ビット)

ピース番号がデコードされた後、ピースのタイプがわかり、各ピースは最も効率的な方法でその動きを表す必要があります。たとえば、(チェスのルールが最初から最後まである場合)ポーンには、合計4つの可能な移動があります(左に移動、右に移動、1つ前に移動、2つ前に移動)。
したがって、ポーンの動きを表すには、「6 + 2 = 8」ビットが必要です。(最初の移動ヘッダーには6ビット、移動には2ビット)

クイーンの移動はより複雑になります。方向(8つの方向、つまり3ビット)と各方向に移動するために移動できる合計8つの可能な正方形(さらに3ビット)を持つのが最善です。したがって、クイーンの移動を表すには6 + 3 + 3 = 12ビットが必要になります。

私が最後に目にするのは、どのプレイヤーがターンしたかを保存する必要があることです。これは単一ビットである必要があります(次に移動するには白または黒)



結果の形式したがって
、ファイル形式は次のようになります。

[64ビット]初期駒の位置
[最大100ビット]初期駒[1ビット]プレーヤーのターン
[nビット]移動

移動が
[1ビット]の場合、移動タイプ(特殊または通常)
[nビット]移動詳細

移動が通常の移動である場合、移動の詳細は
[5ビット]ピース
[nビット]の特定のピース移動(通常は2〜6ビットの範囲)のようになります。

それが特別な動き
である場合、整数型とその後の追加情報(それがキャストされている場合など)を持つ必要があります。必殺技の数が思い出せないので、必殺技であることを示すだけでも大丈夫かもしれません(1つしか無い場合)


1

最初のボードとその後の移動の基本ケースでは、以下を考慮してください。

チェスプログラムを使用して、すべての可能な動きに確率を割り当てます。たとえば、e2-e4の場合は40%、d2-d4の場合は20%などです。一部の移動が合法であるが、そのプログラムで考慮されていない場合は、それらに低い確率を与えます。算術コーディングを使用して、どちらの選択が行われたかを保存します。最初の移動は0から0.4、2番目の移動は0.4から0.6のようになります。

反対側も同じようにします。たとえば、e2〜e4への応答としてe7〜e5の確率が50%の場合、エンコードされた数値は0〜0.2になります。ゲームが完了するまで繰り返します。その結果、範囲が非常に狭くなる可能性があります。その範囲に収まる最小の底を持つ2進分数を見つけます。それが算術コーディングです。

これは、フラクショナルビットエンコーディングと見なすことができるため、ハフマンよりも優れています(さらに、ゲームの最後にビット全体を切り上げるためのいくつかのビットエンコーディング)。

結果はハフマンよりもコンパクトである必要があり、昇格、エンパッサン、50ルールムーブ、およびその他の詳細は、チェス評価プログラムによって処理されるため、特別なケースはありません。

再生するには、再びチェスプログラムを使用してボードを評価し、各手にすべての確率を割り当てます。算術符号化された値を使用して、実際にプレイされたムーブを判別します。完了するまで繰り返します。

チェスプログラムが十分であれば、黒と白の両方の動きに基づいて確率が定義される2ステートエンコーダーを使用して、より良い圧縮を得ることができます。いくつかの200以上の状態の最も極端なケースでは、これはすべての可能なチェスゲームのセット全体をエンコードするため、実行できません。

これは、Dariusがすでに書いたものとはかなり異なる方法であり、算術コーディングがどのように機能するかを示すほんの少しの例と、既存のチェスプログラムを使用して次の動きの確率を評価する実際の例を示しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.