WythoffのNimを完璧にプレイする


16

あなたの目標は、Wythoff's Nimのゲームに最適なプレイヤーを書くことです。

ワイソフのニムのルール

WythoffのNimは、2つの同じカウンターの山でプレイされる決定論的な2プレイヤーゲームです。プレイヤーは交互にターンし、次のいずれかを行います。

  1. 最初のパイルから1つ以上のカウンターを削除します
  2. 2番目のパイルから1つ以上のカウンターを削除します
  3. 最初のパイルと2番目のパイルの両方から、等しい数のカウンター(1つ以上)を削除します。

もちろん、パイルは負になることはできませんが、0にすることはできます。最後のカウンターをすべて削除したプレイヤーがゲームに勝ちます。

より幾何学的に考えるために、このアプレットでプレイできるゲームの同等の定式化を以下に示します。単一の女王は、コーナーが左下にある4分の1の無限チェス盤の正方形から始まります。プレイヤーは交互にクイーンを移動します。クイーンはチェスのクイーンのように移動しますが、3方向に制限されます。

  1. ダウン
  2. 斜め下および左

クイーンをコーナーに移動する人が勝ちます。

クイーンの座標(コーナー(0,0))をそれぞれのパイルのサイズに関連付けると、両方のゲームが同じであることが簡単にわかります。

完璧なプレイ

(完璧なプレーと勝利の動きの概念に精通している場合は、これをスキップできます。)

WythoffのNimは有限で決定的なゲームであるため、完全なプレイという概念があります。完璧なプレーヤーとは、常に理論的に勝ったポジションから勝つ戦略です。つまり、勝利を保証する戦略が存在するポジションです。

勝利戦略となるには、移動したばかりのプレーヤーの理論上の勝利位置に常に移動するように移動するだけで十分です。これらの最初の勝利ポジション(コールドポジションとも呼ばれ(0,0), (1,2), (2,1), (3,5), (5,3)ます)はです。Wythoff's Nimの勝利戦略を見つけるアルゴリズムの説明と、勝利ポジションを生成する公式については、Wikipediaの記事を参照してください。

プログラム要件

プログラムや関数を書くと、入力として位置を取り、その動きの後の位置の形で勝ちの動きを出力します。最少バイトが勝ちます。

勝ち手が存在しない場合、つまりポジションが理論上の損失である場合、プログラムはその旨を示し、没収する必要があります。

プログラムは妥当な時間内に実行する必要があります。したがって、指数関数的再帰ゲームツリー検索では十分ではありません。戦略を事前に計算してハードコーディングしたい場合は、問題ありません。

入力

(i,j)杭のサイズを表す、それぞれ最大で1組の負でない数99。これは、2つの数字、タプル、リスト、または任意のコンテナにすることができます。

出力

移動後の位置を、再び2つの数字またはそのコンテナーとして印刷または出力します。これは勝ちのポジションへの合法的な動きでなければなりません。このような動きが複数ある場合、どれでも構いませんが、1つだけです。

勝つ動きがない場合は、出力でこれを示す必要があります。任意の出力は次のようにFalseNone0、または(-1,-1)それは法的な位置ではありません、すべて負け入力のための同じである限り、行います。

実行例

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

2
+1、無限の四分の一のアイデアが好きだから。
レベルリバーセント14年

「合理的な時間」を定義します。(100,50)の数秒は妥当な時間ですか?
ジョンドヴォルザーク14年

ああ。待つ。入力の境界は... 30 ??? 少し低いですね。
ジョン・ドヴォルザーク

@JanDvorakそうですね、安いショートカットを許可するかもしれません。99に変更-これで十分だと思いますか?投稿後に仕様を編集することをおologiesびします。
xnor 14年

@PeterTaylorありがとう、修正。
xnor 14年

回答:


6

Haskell、167 165文字

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

アルゴリズムは非効率的ですが、100未満の入力に対しては1秒以内に実行されます(ただし、インタラクティブコンソールでは実行されません)。

説明:

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

前のコールドポジションのセットが与えられたコールドポジションのリストは、1つのコールドポジションの後に、このポジションと前のコールドポジションのリストが続きます(非効率:このポジションは2回生成されます)

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

1つのコールドポジションは、そのペアから到達可能なコールドポジションがない最初のペアです(非効率:代わりに前のペアから検索する必要があります)

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

ペアから到達可能な位置は、最初の要素が一致する、2番目の要素が一致する、差分が一致する、または削除前の小さいヒープが削除後の大きいヒープになるような位置です。

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

(主な方法)ヒープの順序が間違っている場合は、スワップします。それ以外の場合、ある位置から到達可能な最初のコールド位置が位置自体である場合、失敗を示します(理想的には、Maybe (Int,Int)代わりに戻ります)。それ以外の場合、コールドポジションを返します(非効率:このペアは2回検索されます。さらに悪いことに、コールドポジションのリストは2回生成されます)。


だから、指数関数的再帰ゲームツリー検索では十分ではない」と楽観的だったようです。
ピーターテイラー14年

@PeterTaylorこれはO(n ^ 4)です。各コールドペアの検索にはO(n ^ 3)時間かかり、O(n)個あります。生成を最適化すると、O(n ^ 2)になります(シーケンスを正しく読み取った場合)。指数時間アルゴリズムは非常に遅くなります。いくつかのテストを実行する必要がありますか?
ジョンドヴォルザーク14年

大丈夫、私はあなたを信じています。
ピーターテイラー

x@から削除できますx@(a,b)?p=...
誇りに思ってhaskeller 14年

どうやってそこに着いたのか分かりません。修正、ありがとう。
ジョン・ドヴォルザーク

5

GolfScript(63 57バイト)

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

フォームのstdinからの入力を期待し、そのフォームの[a b]stdoutに出力を残します0オンラインデモ

概要

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

Beattyシーケンスプロパティを使用して、コールドポジションのリスト([b a]各コールドポジションの反転バージョンを含む[a b])を計算します。

次に?、作成されたブロックを満たす最初のコールド位置を検索します

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

その位置は、ベクトル差を計算し、それがいずれかのだことを確認することによって入力位置から到達可能であることを基本的にチェックし[0 x][x 0]または[x x]いくつかのためにx > 0。:IMOことテストは賢いビットで0|$形式に力それらのいずれかの形式で任意の配列[0 x]マッピングしつつ[0 0][0][a b]ここでもabある03素子アレイに、及び[-x 0]又は[-x -x][-x 0]。次に(!*,1=、持っていることを確認します[0 x]

最後に0]0=`、フォールバックケースと出力のフォーマットを行います。


4

ピス 57 58 61 62

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

オンラインでお試しください。

他の回答とかなり似ていますが、そのウィキペディアのページは他に行くことをあまり与えませんでした;)マジックナンバー39は、値<を持つコールドポジションの数です99

gように呼び出すことができる関数を定義しますg 30 25。成功した[]場合、失敗した場合[(13,8)]に戻ります。

説明

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

sはintにキャストされ、数文字を保存し/____1ます。単項範囲を使用rZ39してU39、に置き換えることができます。同様に、あなたは置き換えることができr99)U99
isaacg 14

@isaacgありがとう!私は完全に忘れていましたU。P:私は本当に説明更新する必要があります
FryAmTheEggman

@isaacg Pythについて考えただけで、@2番目の引数がリストになっている場合は、交差点を実行できると思います。以来、それは少しぎこちなくて残ってa変更されました:P
FryAmTheEggman

それは良いアイデアです-私はそれを実装しました。また、交差点のコードを変更して、2つのリストの交差点を取得するなど、これまで不可能だったいくつかのトリックを許可しました。
isaacg 14

2

Javascript ES6-280バイト

縮小

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

拡大

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

素晴らしく迅速なアルゴリズム。O(n)で実行されますが、バイト節約ループでない場合は一定時間で実行されます。ループは再帰的なインクリメンターとして実装されているため、最終的にスクリプトは数百または数千のnの再帰エラーで失敗します。Taylor氏が言及したのと同じBeattyシーケンスプロパティを使用しますが、シーケンスを計算するのではなく、単に正しい用語に進みます。

すべてのテスト入力と、私がテストしたもの以外の多くの入力で適切に実行されます。

呼び出す関数はfです。成功すると配列を返し、0giving めると配列を返します。


待って、配列を出力してもいいですか?
ジョン・ドヴォルザーク

@JanDvorak:xnorには有効な出力のリストにリストされたタプルがあります。たぶん彼は問題を明確にすることができます。とにかく些細な修正です。
COTO

ペアの配列またはシングルトン配列は問題ありません。複数の勝ち手はありません。
XNOR

1

Perl 5-109(2つのフラグ付き)

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

使用法:

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

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

可能な入力ごとに単純に解を計算し、単一のルックアップを実行します。

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