チャンピオンになる


11

三目並べ!

これは実話なので、名前は変更されています。

ラテン語の先生であるラテン氏は、独自の(冗談ではない)三目並べを作成しました。tic-tac-latinと呼びましょう。ゲームはシンプルで、基本的に三目並べで4 x 4のグリッドでプレイされます。

正式なルール宣言

ラインは、行、列、または対角線のいずれかです。「X」と「O」の2つの記号がありますが、1つまたは両方を別の記号に置き換えることができます。
シンボルを3つと他のキャラクターを1つ持っているときに1ポイントを獲得します。

これらのアレンジメントのスコア:

--- O
-O--
XXXO
XOOX

O -XX
- O -
- X -
--- O

これらは得点しません:

----
XXXX
----
OOOO

----
XXX-
----
OOO-

1人のプレイヤーが他のプレイヤーよりも多くのポイントを獲得するたびに、ゲームに勝ちます。ボードがいっぱいになった場合にのみ、ゲームは引き分けです。

チャレンジ

このゲームを解決してください。あなたの仕事は、勝ちと引き分けのどちらか最適な結果を保証する方法を提供することです。

ソリューションは、最初に開始するか、2番目に開始するかを選択できます(したがって、そのシンボルを選択できます)。ユーザー入力が移動し、対応する表示が変化するインタラクティブなゲームを実装することは必須ではありません。また、ゲームの状態として入力を受け取り、新しいボードまたはそれらの動きの説明を出力する関数またはプログラムでもあります。どちらのオプションも、移動ごとに約10秒以内に実行する必要があります。


一連の動きに対してプレーヤーをプレイすると、最適な結果が得られます。これは、入力位置がプレーヤーでのプレイから到達可能な位置であると想定できることを意味します。提出物は決定論的である必要があり、必ずしも最適性の証拠を提供する必要はありませんが、(打たれることにより)クラックされる場合、あなたの提出物は無効とみなされます(あなたはそれを残すことができますが、見出しに(クラックする)を追加します)。
これは簡単な作業ではないので、有効な提出は印象的であり、受け入れられたダニの価値がありますが、私はコードゴルフを主要な勝利基準にします。

勝者は、1人の勝者が選択されるまでこのリストを下って行くことによって選択されます。

  • 常に勝つ最短の解決された実装
  • 最短の実装

1
「最初にプレイの質を調べる」主観的だと思いませんか?
user48538

プレイするためのインターフェースを提供するタスクは、完璧なプレイヤーを書くことの周辺にあるようです。現在のゲームの状態を単に入力として渡し、勝利の動きを出力するためのコードを要求するか、完全なプレイ(勝利、引き分け、敗北)での単純な評価でさえもお勧めします。
xnor

1
解決策は、非効率的なブルートフォース検索を実行することにより、ゴルファーになる可能性があります。コードの実行が非常に遅い場合でも大丈夫ですか?
xnor

1
あなたが得点し、その過程で相手に得点しなかった場合、あなたはゲームに勝ちます。」これは、私が駒を置いたときにのみ勝つことができることを意味します。移動によって両方のプレイヤーの勝ち線が作成された場合はどうなりますか?
ピーターテイラー

1
@RohanJhunjhunwalaゲームの状態の許可された入力を明確にする必要があります。そうしないと、現在定義されていない入力形式を利用して、ソリューションに役立つ形式を選択できる可能性があります。
ASCIIのみ

回答:


6

Perl、147バイト(非競合、1移動あたり10秒以上かかります)

+4を含む -0p

プログラムが再生されXます。それは完璧なゲームをプレイします。

STDINでボードを入力します。例:

tictaclatin.pl
-X-O
-X--
X-X-
O--O
^D

ouptutは同じボードで、すべてがX置き換えられO、その逆も同様です。空のスポットは、Xがそこでプレーする場合の結果を示す数字で埋められ1、結果は勝ち、2引き分け3、負けとなります。終了したゲームは、色を逆にして同じ位置に戻るだけです。

この例では、出力は次のようになります。

1O1X
1O33
O3O3
X33X

そのためX、彼が左上にある3つのスポットでプレーすれば、そのポジションは勝ちです。他のすべての動きは負けます。

この混乱する出力は、移動後のゲームの継続方法を知りたい場合に実際に便利です。プログラムは常にプレイXするので、交換XOて、の動きを見る必要がありますO。たとえばX、左上でプレーすることで勝つことは明らかですXが、3番目の位置でプレーする場合はどうでしょうか?出力をコピーし、O選択した移動の代わりに置き、他のすべての数字を-再度置き換えてください。

-OOX
-O--
O-O-
X--X

その結果:

3XXO
3X33
X3X3
O33O

明らかにすべての動きOは負けるはずです。左上でプレーするとどうやって負けますか?再びこれを行うにOは、左上に配置し、数字を-次のように置き換えます。

OXXO
-X--
X-X-
O--O

与える:

XOOX
1O33
O3O3
X33X

そのため、Xには勝つための唯一の方法があります。

XOOX
OO--
O-O-
X--X

与える

OXXO
XX33
X3X3
O33O

の状況Oは絶望的です。すべての動きがXすぐに勝つことができるようになったので、簡単にわかります。少なくとも3つのOを続けて試してみましょう。

OXXO
XX--
X-X-
O-OO

与える:

XOOX
OO13
O3O3
X3XX

X勝つ唯一の動きを再生します(これはXXXO3番目の列に沿って行います:

XOOX
OOO-
O-O-
X-XX

出力は次のとおりです。

OXXO
XXX-
X-X-
O-OO

ゲームはすでに終了しているためです。3列目で勝利を確認できます。

実際のプログラムtictaclatin.pl

#!/usr/bin/perl -0p
y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/sx;$@<=>0||s%-%$_="$`O$'";$$_||=2+do$0%eg&&(/1/||/2/-1)

空のボードに適用すると、9506699の位置が評価され、コンピューターで30Gbと41分かかります。結果は次のとおりです。

2222
2222
2222
2222

したがって、すべての開始移動は引き分けです。だからゲームは引き分けです。

極端なメモリ使用量は、主にを使用した再帰によって引き起こされdo$0ます。プレーン関数を使用してこの154バイトバージョンを使用するには、3Gbと11分が必要です。

#!/usr/bin/perl -0p
sub f{y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/sx;$@<=>0||s%-%$_="$`O$'";$$_||=2+&f%eeg&&(/1/||/2/-1)}f

これはより耐えられます(ただし、まだ多すぎるため、何かがメモリをリークしている必要があります)。

多くの高速化を組み合わせることで、この160バイトバージョン(5028168ポジション、4分、空のボードで800M)になります。

#!/usr/bin/perl -0p
sub f{y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/osx;$@<=>0||s%-%$_="$`O$'";$a{$_}//=&f+1or return 1%eeg&&/1/-1}f

最後の1つは0、勝利(混同しないでくださいO)、1引き分け、および2損失に使用します。これの出力も混乱を招きます。カラースワップのない勝利の場合、Xの勝ちの動きを埋めますが、入力ゲームが既に勝った場合は色のスワップを行い、動きを埋めません。

もちろん、ボードがいっぱいになると、すべてのバージョンが高速になり、使用するメモリが少なくなります。高速バージョンでは、2〜3回の移動が行われるとすぐに10秒以内に移動が生成されます。

原則として、この146バイトバージョンも動作するはずです。

#!/usr/bin/perl -0p
y/XO/OX/,$@=-$@while/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^/sx,--$|;$@<=>0||s%-%$_="$`O$'";$$_||=2+do$0%eg&&(/1/||/2/-1)

しかし、私のマシンではperlのバグを引き起こし、コアをダンプします。

によって行われた6バイトの位置キャッシュ$$_||=が削除された場合、すべてのバージョンは原則として引き続き機能しますが、ほとんどのボードでのみ機能するほど多くの時間とメモリを使用します。しかし、理論的には少なくとも140バイトのソリューションがあります。

あなたが置く場合$\=(コスト:3バイト)の直前に$@<=>0、各出力ボードは、ボード全体の状態が続きます:1ためXの勝利、0引き分けと-1損失のために。

上記の最速バージョンに基づく対話型ドライバーを次に示します。ドライバーには、ゲームが終了したときのロジックがないため、自分で停止する必要があります。しかし、ゴルフのコードは知っています。提案された動きが-何にも置き換わることなく戻る場合、ゲームオーバーです。

#!/usr/bin/perl
sub f{
    if ($p++ % 100000 == 0) {
        local $| = 1;
        print ".";
    }
y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/osx;$@<=>0||s%-%$_="$`O$'";$a{$_}//=&f+1or return 1%eeg&&/1/-1}

# Driver
my $tomove = "X";
my $move = 0;
@board = ("----\n") x 4;
while (1) {
    print "Current board after move $move ($tomove to move):\n  ABCD\n";
    for my $i (1..4) {
        print "$i $board[$i-1]";
    }
    print "Enter a move like B4, PASS (not a valid move, just for setup) or just press enter to let the program make suggestions\n";
    my $input = <> // exit;
    if ($input eq "\n") {
        $_ = join "", @board;
        tr/OX/XO/ if $tomove eq "O";
        $p = 0;
        $@="";
        %a = ();
        my $start = time();
        my $result = f;
        if ($result == 1) {
            tr/OX/XO/ if $tomove eq "O";
            tr/012/-/;
        } else {
            tr/OX/XO/ if $tomove eq "X";
            tr/012/123/;
        }
        $result = -$result if $tomove eq "O";
        my $period = time() - $start;
        print "\nSuggested moves (evaluated $p positions in $period seconds, predicted result for X: $result):\n$_";
        redo;
    } elsif ($input =~ /^pass$/i) {
        # Do nothing
    } elsif (my ($x, $y) = $input =~ /^([A-D])([1-4])$/) {
        $x = ord($x) - ord("A");
        --$y;
        my $ch = substr($board[$y],$x, 1);
        if ($ch ne "-") {
            print "Position already has $ch. Try again\n";
            redo;
        }
        substr($board[$y],$x, 1) = $tomove;
    } else {
        print "Cannot parse move. Try again\n";
        redo;
    }
    $tomove =~ tr/OX/XO/;
    ++$move;
}

いい答え。これをテストする簡単な方法を提供していただけますか?理想的には、インタラクティブなバージョンを見たいと思います...(これは私自身の好奇心のために重要です)。
ローハンジュンジュンワラ

@RohanJhunjhunwala OK、シンプルなインタラクティブドライバーを追加
Ton Hospel

変数「$移動」がprog.pl:2で宣言されていません
ロハンJhunjhunwala

人間が実装できる発見的解決策はありますか?
ローハンジュンジュンワラ

@RohanJhunjhunwalaドライバープログラムを再確認しました。正常に実行$moveされ、11行目で宣言されています。人間のヒューリスティックがあるかどうかはわかりません。このプログラムはゲームツリーでミニマックスを実行するだけで戦略的な知識はありません。
トンホスペル16

2

JavaScript(ES6)392バイト

a=>b=>(c="0ed3b56879a4c21f",r=[],k=f=>r.push([a.filter(f),b.filter(f)]),[0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i)),k(n=>n%5==0),k(n=>n&&n-15&&!(n%3)),g=r.find(o=>(o[0].length==1&&o[1].length==2)||(o[0].length==2&&o[1].length==1)),g?parseInt(c[30-[...g[0],...g[1]].map(i=>parseInt(c[i],16)).reduce((p,c)=>p+c)],16):[...a,...b].indexOf(15-a[0])+1?15-a.find(i=>b.indexOf(15-i)==-1):15-a[0])

使用法

「ボット」が2番目に再生されます。

次のように番号が付けられた4x4グリッドを描画します。

+----+----+----+----+
|  0 |  1 |  2 |  3 |
+----+----+----+----+
|  4 |  5 |  6 |  7 |
+----+----+----+----+
|  8 |  9 | 10 | 11 |
+----+----+----+----+
| 12 | 13 | 14 | 15 |
+----+----+----+----+

ブラウザコンソールでこれを実行してみましょう。f=コードの前に置くだけです

だから、もし始めたいなら1、私は走りf([1])([])、それが私に与える14

いい動きだ... 2あとでプレイしたらどうなる?f([2,1])([14])。戻り13ます。

レムは降伏してみてください。再生し3ます。f([3,2,1])([14,13])。ああ0!まいったよ!

遊ぶ0f([0,2,1])([14,13])15じゃあ遊び続けましょう...

注意

  1. インタラクティブにプレイします。で始まりf([your-step])([])ます。

  2. 次のステップを準備します。(上記のデモを参照)

  3. 「ボット」のステップ入力を支援します。ランダムに設定すると、良い結果が得られません。(Like f([1,2,4])([14,12])will 14give-ちょっとボットは132番目の動きでプレイしたかった!

簡単な要約

あなたが降伏していない限り、ボットは鏡の動きをします。

ゲームのルールとゴルフのヒントを読み間違えたと言ってくれてありがとう@EHTproductions:P

また、チェックメイトになったかどうかも検出します。はいの場合は、ブロックしてください!

その優先順位:ブロック>ミラー>(フォールバック)ミラーを再現する方法を探す


私は本当に「ミラームーブ」戦術が好きです:)私は誤解しているかもしれませんが3,2,1、あなた0にとってもボットにとっても勝利ではありませんか?
-ETHproductions

おっと、「ある種の3と別の1のパターンをキャプチャする人」と誤解されました。Lemmeがソリューションを少し調整します。@ETHproductionsに感謝します。
サニーしゃれ

いくつかのゴルフのヒント:[0,1,2,3].map(i=>{k(n=>n%4==i);k(n=>Math.floor(n/4)==i);})にゴルフできます[0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i))
ETHproductions

私は、これは証明可能勝算はないと思います
ロハンJhunjhunwala

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