戦争ゲームの結果を見つける


15

戦争ゲームの結果を見つける

私が小学校にいたとき、「ロック紙はさみ」のようなゲームがありました。集会中、先生を待つとき、休憩時間などにプレイしました。「戦争」と呼びました。いくつかの検索の後、これは「ショットガンゲーム」(WikiHowによる)のはるかに単純なバリアントであることがわかりました。ルールがわずかに異なるため、これを「戦争」と呼びます。

2人が向かい合って座っています。ゲームの目的は、他のプレイヤーを「殺す」ことです。ターンごとに、次の3つの動きのいずれかをプレイできます。

  • リロード:あなたは一発の銃を持っています。毎回起動する前にリロードする必要があります。すでに弾薬を持っている場合のリロードは合法ですが、何もしません。リロードは、両手でこめかみをタップすることで象徴されました。各プレイヤーは弾薬0から始まります。

  • ガード:唯一の安全な動き。警備中に撃たれても、死なない。ガードは、胸に腕を組むことで象徴されました。

  • 発射:銃を発射します。発射に成功するには、最後のショットからリロードする必要があります。相手がリロードしている場合、あなたが勝ちます。彼らも発砲し、あなたの両方が弾薬を持っている場合、それは引き分けです。彼らが警備している場合、弾薬を無駄にします。弾薬なしでの発砲は合法的な動きですが、何もせず、リロードのような脆弱性を残します。発射は、他のプレイヤーを指すことで象徴されました。

RPSと同様にプレイされました。各プレイヤーが同時に選択をスローダウンしました(互いのリズムを保つために、ターンの間に足を2回タップしましたが、それはチャレンジにとって重要ではありません)。

チャレンジ:

あなたの仕事は、戦争ゲームの結果を見つけることです。関数または完全なプログラムを指定できます。

入力

  • 各プレイヤーが各ターンで選択したオプションは、文字/文字列で表されます:

    • r:リロード

    • g:ガード

    • f:火

  • 入力は、ペアのリスト、区切られた/区切られていない文字列、またはこれらの行に沿ったその他のものです。

Pythonの入力例として、があります[("r", "g"), ("f", "r")]。これは、最初のターンで最初のプレイヤーがリロードし、2番目のプレイヤーがガードしたことを意味します。2番目のターンで、最初のプレイヤーが射撃し、2番目のプレイヤーがリロードします。プレイヤー1がこのゲームに勝ちます。同じ入力は、必要に応じとして表すことができ"r g f r""rgfr""rg fr" "rg-fr"...

次のことを想定できます。

  • 入力は選択した形式と一致し、有効な文字のみが含まれます。

  • 誰かが100ターン以内に死にます。

ただし、誰かが死んだときにターンが終了すると想定することはできません。

出力

誰が勝ったか(または誰が最初に勝ったか)を示す値*。各シナリオで何を出力するかを選択できますが、次のことを考慮する必要があります。

  • プレイヤー1が勝利

  • プレイヤー2が勝利

  • 彼らはお互いを殺します(引く)

各結果には地区値が必要であり、各シナリオで常に同じでなければなりません。

例として、1プレーヤー1が勝った2とき、プレーヤー2が勝ったとき、および0引き分けの場合に出力できます。プレーヤー1が勝ったとき、プレーヤー2が勝ったとき、および引き分けの場合は常に出力する必要があります。120

返されるか、標準出力に出力されます。末尾の空白は問題ありません。

明らかなように、引き分けにつながる唯一のシナリオは、両方のプレイヤーが射撃し、両方が弾薬を持っている場合です。

*このチャレンジでは、誰かが死亡した後もターンが続く可能性があるため、最終的には1人以上のプレイヤーが勝つ可能性があります。入力に従って最初に勝った人を見つける必要があります。

テストケース(1P1が勝った2とき、P2が勝ったとき0、引き分けの場合):

"rg fr" => 1 (P1 shot P2 while they were reloading)

"rg ff" => 1 (They both shot, but only P1 had ammo)

"rr ff" => 0 (Both had ammo and shot each other)

"rr ff rr fg" => 0 (Both had ammo and shot each other. Everything after the first win is ignored)

"rr fg rf" => 2 (P2 shot P1 while they were reloading)

"rf gg rr fg rr fr" => 1
    (P2 tried to shoot but didn't have any ammo, then they both guarded, then they both reloaded, then P2 blocked a shot, then they both reloaded again [but P2 still only has 1 ammo!], then P1 shoots P2 while they're reloading.

"rr gf fr rf gg rg ff" => 1
       ^ Player 1 wins here. The rest to the right has no effect on the output

これはコードゴルフなので、最小バイト数が勝ちです!

テストケースが示すように、「ダム」移動を処理する必要があることに注意してください。プレイヤーが弾薬を持っていないときに射撃を試みるか、2ターン連続でリロードする(そして弾薬を1つだけ蓄積する)ことは完全に有効です。


何か不足しているのですか、それとも最終ラウンドから出力を決定できますか
XNOR

@質問を更新しました。いいえ。プレイヤーに弾薬があるかどうかを知る必要があるためです。しかし、最後のターンであるという事実に基づいて、どのプレイヤーが弾薬を持っているかを推測できることを理解しています。誰かが死んだときに入力が終了すると仮定できるように、実際にルールをかなり最後に変更しました。私は今それを後悔しています。
発がん性


3
これは、ロード、ディフェンド、シュートのゲームのスコアリングに非常に似ています。唯一の違いは、他のチャレンジには複数のショットを持つ銃があり、空の銃を撃つことは不正行為と見なされ、ゲームを没収することです。
デニス

ラウンドではなく、2人のプレイヤーに対して2つの別々の入力を取得できます{"rff","rgf"}か?
betseg

回答:


2

網膜、36バイト

s`(?<=r..([^f]..)*)f
!
A`g
G1`!
\w
_

入力形式は、改行で区切られたペアにする必要があります。

rr
fr

出力は!__!プレーヤー2が勝ち!!、引き分けがある場合、プレーヤー1が勝ちです。

オンラインでお試しください!(便宜上、スペース区切りを使用するテストスイート。)

私はこの課題を完全に見落としていたに違いありません。そうでなければ、以前にRetinaでこれを試したはずです。:)

説明

s`(?<=r..([^f]..)*)f
!

私たちは、最初に回して「有効」ショットをマークすることによって開始しf、それぞれの後r!。これを行うには、同じプレーヤーで別のを越えることなくをf見つけることができる各を一致さrfます。r同じプレーヤーで検索をsに制限することは、一度に3つのキャラクターを常に移動させることにより簡単です。

A`g

ここで、誰かが自分を守ったすべてのターンを破棄します。なぜなら、終了ターンはそれらの1つではないからです。

G1`!

ここ、を含む最初のターンのみを保持します!。有効なショットが発生した場合(そして誰もガードしていないことがわかっている場合)、ゲームは終了します。

\w
_

最後に、一貫した出力を得るために文字列を統合する必要があります。これは、非!文字(rまたはf)をに変換することで簡単に行え_ます。


5

Python、139バイト

c=d=0
for i in input():
 b=(c&(i=='fr'))-(d&(i=='rf'));p,q=i
 if b|(i=='ff')&c&d:print b;break
 c,d=(p=='r',i!='fg')[c],(q=='r',i!='gf')[d]

2文字の文字列のリストの形式で標準入力を入力します(例:['rf'、 'rr'、 'rg'、 'ff'])。プレーヤー1が勝った場合は1、プレーヤー2が勝った場合は-1、引き分けの場合は0を出力します。

説明:まず、誰かが弾丸を発射したかどうかを確認し、発射した場合はゲームを終了します。次に、プレイヤーが銃をリロードしたか弾薬を無駄にしたかを判断します。

これは私の最初のcodegolf投稿です:)


4

JavaScriptの(ES6)、108の 107 93 91 89 85バイト

Titusの助けを借りて4バイトを保存

各プレイヤーがプレイした動きを表す2文字の文字列の配列として入力を受け取ります。

b=>b.map(c=>w=w||b&'312'[b=(s='0210231')[m='ffrfgrrggf'.search(c)]|s[m-2]&b,m],w=0)|w

戻り値:

  • 1 プレイヤー1が勝った場合
  • 2 プレイヤー2が勝った場合
  • 3 引き分け

使い方

b誰が弾丸を装填したかを示すビットマスクを維持します:

  • ビット#0:プレーヤー1に弾丸があります
  • ビット#1:プレーヤー2に弾丸があります

De Bruijnシーケンス を使用して'ffrfgrrggf'、動きの9つの可能な組み合わせすべてを識別します。ORおよびANDビットマスクを使用bして、移動の組み合わせに従って更新します。b勝者を決定するためにANDで結合されたビットマスクの3番目のセットを使用しますw。(唯一の3つの勝ちの組み合わせはfffrおよびrfです。)

ORマスクとANDマスクを同じパターンで保存し、2つの位置だけシフトできることに注意してください。

 Index in | Combination | Bullet   | Bullet  | Winner
 sequence |             | AND mask | OR mask | mask
----------+-------------+----------+---------+--------
    0     |     ff      |    0     |    0    |   3
    1     |     fr      |    0     |    2    |   1
    2     |     rf      |    0     |    1    |   2
    3     |     fg      |    2     |    0    |   0
    4     |     gr      |    1     |    2    |   0
    5     |     rr      |    0     |    3    |   0
    6     |     rg      |    2     |    1    |   0
    7     |     gg      |    3     |    0    |   0
    8     |     gf      |    1     |    0    |   0

テストケース


@Carcigenicateこれは、両方の失敗した場合に修正する必要があります。しかし、引き分けの場合、私は今0(誰も撃たれなかった)または3(プレイヤーがお互いを殺す)に戻っています。これが許可されているかどうかはわかりません。そうでない場合は、w%3代わりに戻ることができます。
アーナウド

シナリオごとに1つの出力が必要でした。誰かが常に撃たれることを保証しますので、その場合の会計は不要です。引き分けにつながる唯一のケースは、両方がお互いを撃ち、両方が弾薬を持っている場合です。
発癌性

&マスクは0可能fr,rf,ff'312'['0210231'[m='ffrfgrrggf'.search(c)]|'233331'[m-3]&b]または'123'['2100231'[m='frffgrrggf'.search(c)]|'233331'[m-3]&b]1バイトを保存します。しかし、彼らは動作しますか?
タイタス

@Titus興味深いことに、ANDマスクの前にORマスクを適用すると、既存のすべてのテストケースで機能します。しかし、それは次のような何かのために失敗します["rr","fg","fr","rf"]
アーナウド

&の優先順位はの場合よりも高い|ため、順序を変更してもそこに何も変更されるべきではありません(バイトの保存は別として)。しかし、割り当ては私のコードにありませんでした。試してください...'123'[b='2100231'...
タイタス

2

Perl 6の71の 62バイト

{&[<=>](|map {m/r[..[r|g]]*.$^f/.to//∞},/[r|f]f/,/.f[r|f]/)}

正規表現ベースのソリューション。

フォームの文字列として入力を受け取ります"rg fr"
三つの可能な出力は、列挙値であるMore(プレーヤ1ウォン)、 Less(プレイヤー2ウォン)、Sameそれらの単語にターン印刷、またはに- (描画)1-10番号に強制します。

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

使い方

  • map { m/r[..[r|g]]*.$^f/.to // ∞ }, /[r|f]f/, /.f[r|f]/

    入力に対して2つの正規表現一致を実行します。補間後、2つの正規表現は次のとおりです。

    • r[..[r|g]]*.[r|f]f –プレーヤー2が最初に成功したショットと一致します。
    • r[..[r|g]]*..f[r|f] –プレーヤー1が最初に成功したショットと一致します。

    いずれの場合も、一致の終了位置(.to)を返します。一致しなかった場合は無限を返します。

  • &[<=>](|   )

    <=>一致する2つの終了位置に演算子を適用します。それから値を返すOrder(列挙MoreLessまたはSame)、最初の引数が大きいか否かに応じて、より少ない、又は第二に等しいです。


きちんとした。好奇心から、無限大記号をどのように入力しますか?特別なキーボード、またはalt +を入力しますsome numberか?そして、あなたは実際に一般的なPerlコードでそのような文字を使用していますか、それともゴルフのためだけですか?
発癌性

@Carcigenicate:カスタマイズされたキーボードスキームでは、4つのキーを押すことで入力できます[Menu] i n fコンポーズシーケンスと呼ばれます)。ただし、すべてのPerl 6シンボルにはASCIIバージョンがInfあり、たとえば、同義語です。したがって、Perl 6コードでUnicodeシンボルを使用する必要はありません。私はそれが好きです... :)
smls

ああ。Perlについて私に投げかけられたものの1つは、無限大記号でした。私はそれが必要だと思ったが、それは不必要に複雑に思えた。たぶん、Clojureに飽きたら、Perlを試すでしょう。最近、多くのPerlコードを見てきました。
発癌性

@Carcigenicate:Perl 6は基本的にPerlと後方互換性のない新しい言語であり、そのインタープリターはまだ遅いことに注意してください(一部の機能ではバグがあります)。現在バージョンv5.24のPerlは、引き続き個別に保守されます。
smls

はい、ありがとう。知っておくといい。
発癌性

2

Haskell101 91 87バイト

n!(c:r)|'g'>c=n:1!r|'g'<c=1:0!r|1<3=2:n!r
_!r=[]
a#b=[x|x@(y,z)<-zip(1!a)$1!b,2>y+z]!!0

オンラインでお試しください!infix関数#は、2人のプレイヤーそれぞれのアクションを表す2つの文字列を取り(0,1)、プレイヤー1が勝った場合(1,0)、プレイヤー2と(0,0)引き分けの場合に戻ります。

使用例:

Prelude> "rgrfrf" # "fgrgrr"
(0,1)

説明:

infix関数!は、一連のアクション'r'(リロード)、'f'(ファイア)、および'g'(ガード)を一連の観察可能なアクション0(実際のファイア)、1(アクションなし)、および2(ガード)に変換します。ファイアアクションは実際のファイアアクションとしてのみカウントされます。弾丸がロードされている場合、およびアクションがない場合。これを達成するための最初の引数n0、弾丸が装填されている1かどうか、銃が装填されていないかどうかです。このようにして、それぞれ'f'を単純に現在のに置き換えることができnます。(n=0->ロード-> 実際の射撃 -> 0n=1->アンロード-> アクションなし -> 1

n ! (c:r)                -- n is 0 or 1, c is 'f', 'g' or 'r' and r the rest of the string
    |'g'>c = n : (1 ! r) -- c is smaller 'g', so it must be 'f'. append n to the list
                         --  and set load status to 1 (unloaded)
    |'g'<c = 1 : (0 ! r) -- c is larger 'g', so it must be 'r'. append 1 (no action)
                         --  and set load status to 0 (loaded)
    |1<3   = 2 : (n ! r) -- c must be equal to 'g'. append 2 (guard)
                         --  and leave the load status unchanged
_ ! r = []               -- base case for recursion

結果として生じる9つの可能性は

  • (0,0):両方のプレイヤーが射殺し、ゲームは終了します。
  • (0,1)または(1,0):1人のプレイヤーが他のプレイヤーを撃ち、ゲームが終了します。
  • (0,2)または(2,0):1人のプレイヤーがシュートしますが、他のガードはゲームを続行します。
  • (1,1)(1,2)(2,1)または(2,2):いいえプレーヤーのシュート、ゲームが続行されます。

設計により、ゲーム終了オプションの合計は2より小さく、各ゲームの継続可能性の合計は2以上です。ゲームの結果は、合計が2より小さい最初のタプルです。

a#b=[x|         -- build the list of all x
    x@(y,z) <-  -- where x is an alias for the tuple (y,z) which is drawn from the list
    zip (1!a)   -- of tuples where the first component is from 1!a = eg. [1,2,1,0,1,0] 
        (1!b)   -- and the second from 1!b = eg. [1,2,1,2,1,1]
    , 2 > y+z]  -- and y+z are smaller 2.
    !!0         -- return the first element of this list

1

バッチ、249バイト

@echo off
set g=goto gg
set/ax=y=0
:gg
shift&goto %1
:fg
set x=0
%g%
:gf
set y=0
%g%
:rr
set/ax=y=1
%g%
:fr
if %x%==1 exit/b1
:gr
set y=1
%g%
:rf
if %y%==1 exit/b2
:rg
set x=1
%g%
:ff
set/az=3-x-x-y
if %z%==3 %g%
exit/b%z%

入力は各ターンのキャラクターのペアの形式で、エラーレベル(0 =描画、1 =プレイヤー1、2 =プレイヤー2)ごとに出力されます。xそしてyその両方の火が、結果があるときに、プレイヤーは弾薬を持っているかどうかを追跡3-x-x-y我々は続ける、その場合には、それが3である場合を除き、。5行目%1では、バッチのパーサー(現在の移動)を悪用し、shiftステートメントが実行されて削除される前に置換されるため、正しいラベルに移動します。


1

Clojure、168バイト

#(reduce(fn[[l L r R][a A]](if(and l L)(let[M(fn[r a A](if(and(= a \f)r)[nil(= A \g)][(or(= a \r)r)1]))[r L](M r a A)[R l](M R A a)][l L r R])[l L r R]))[1 1 nil nil]%)

ゴルフが少ない(両方の人が生きている場合は使用します) M、弾薬と敵のライフ状態を更新ためします。そうでない場合は、現在のステータスを返します):

(def f (fn[A] (reduce
                (fn [[l1 l2 r1 r2] [a1 a2]]
                  (if (and l1 l2)
                    (let[M (fn [r1 a1 a2]
                             (if (and(= a1 \f)r1)
                               [false (= a2 \g)]        ; we lost the ammo, a2 lives if he was guarding
                               [(or(= a1 \r)r1) true])) ; we might gain or keep ammo, a2 lives no matter what
                         [r1 l2] (M r1 a1 a2)
                         [r2 l1] (M r2 a2 a1)]
                      [l1 l2 r1 r2])
                    [l1 l2 r1 r2]))
                [true true false false] A)))

使用例(最初の要素はプレイヤー1がゲーム終了時に生きているかどうかを示し、2番目の要素はプレイヤー2が生きているかどうかを示し、3番目と4番目は勝者を決定する際に関係のない弾薬ステータスを示します):

(-> (for[[a b s] (partition 3 "rr fg rf fr ")][a b]) f (subvec 0 2))

更新:よく見てください、これloopは同じ長さです!をreduce使用すると、中間状態を簡単に検査できるため、バージョンの開発が容易になりますreductions

#(loop[l 1 L 1 r nil R nil[[a A]& I]%](if(and l L)(let[M(fn[r a A](if(and(= a \f)r)[nil(= A \g)][(or(= a \r)r)1]))[r L](M r a A)[R l](M R A a)](recur l L r R I))[l L]))

待っていた!すごいね。
発癌性

Heheおかげで、私はまだ[l1 l2 r1 r2](変更された値letと元の値)とそれらのfn署名を繰り返す必要があることをいらいらさせます。
ニコニール

少なくとも後者については、それが私が好む理由ですloop。私はそれがよりきれいなコードにつながると思います。複数のアキュムレーターでフォールドする必要があるとすぐに、切り替えます。
発がん性物質

1

PHP、107 101 90バイト

ロードステータスにビットマスク$ dを使用し、発射移動にDeBruijnシーケンスを使用します。

for(;!$x=$d&strpos(_frff,$m=$argv[++$i]);)$d=$d&g<$m|h<$m|2*($d/2&f<$m[1]|g<$m[1]);echo$x;

入力を2文字のコマンドライン引数として受け取り、で実行し-nrます。

1 =プレイヤー1が勝利
2 =プレイヤー2が勝利
3 =引き分け

壊す

for(;!$x=$d&strpos(_frff,       // 1. $x=someone dies, loop while not
    $m=$argv[++$i]          // loop throug moves
);)
    $d=
        $d&g<$m|h<$m            // 2. unload/reload Player 1 = bit 0
    |2*(
        $d/2&f<$m[1]|g<$m[1]    // 3. unload/reload Player 2 = bit 1
    );
echo$x;
  • DeBruijnシーケンス::frposition = 1 = P1 fires; rf=位置2 = P2が発動、ff =位置3 =両方が発火
  • g<$m<=> f<$m[0]f<$m2番目の文字があるため、常にtrueです)。

0

Python、200バイト

def war_game(turns):
    turn=0
    player1=True
    player2=True
    ammo1=False
    ammo2=False
    while turn<len(turns):
        if turns[turn][0]=='f' and ammo1==True and turns[turn][1]!='g':
            player2=False
        elif turns[turn][0]=='f' and turns[turn][1]=='g':
            ammo1=False
        elif turns[turn][0]=='r':
            ammo1=True
        if turns[turn][1]=='f' and ammo1==True and turns[turn][0]!='g':
            player1=False
        elif turns[turn][1]=='f' and turns[turn][0]=='g':
            ammo2=False            
        elif turns[turn][1]=='r':
            ammo2=True
        if player2==False or player1==False:
            break
        turn+=1
    if player1==True and player2==False:
        print('Player 1 wins')
        return 1
    elif player1==False and player2==True:
        print('Player 2 wins')
        return 2
    print('Draw')
    return 0

2
サイトへようこそ。コンテストはバイトカウントで採点されるため、タイトルにバイトカウントを含め、変数名の長さを減らして最小化することをお勧めします。また、これを記述した言語を含める必要があります(私にはPython3のように見えます)。
ポストロックガーフハンター

2
上記のように、これはタスクを達成するために可能な限り最小のプログラムを作成できる競争です。turnsjustの代わりにフルネームを使用しています。tこれは、プログラムが必要以上に大きいことを意味します。また#Python 2, 200 bytes、使用している言語が明確になるように(これが2で、プログラムの長さが200バイトであると仮定して)のように送信を開始してください。
発癌性

0

Clojure、180 173バイト

(fn[t](loop[z nil x nil[[c v]& r]t](let[k #(and %3(= %\f)(not= %2\g))h #(and(not= %\f)(or %2(= %\r)))q(k v c x)w(k c v z)](cond(and q w)0 q 2 w 1 1(recur(h c z)(h v x)r)))))

マクロを使用する代わりに、関数を完全な関数に変更することにより、-7バイト。これにより、内部関数マクロを作成でき、少し節約できます。

これは非常に文字通りの解決策です。ゲームのフルバージョンを書いたばかりなので、私はちょっと心が動かされません。これは基本的に、使用したアルゴリズムの大幅に削減されたバージョンです。おそらく私ができる最適化はたくさんありますが、私はそれでかなり満足しています。説明については、事前に作成されたコードを参照してください。

(defn outcome [turns] ; Take input as ["rr" "ff"]
  (loop [p1-ammo? false ; Keep track of if each player has ammo
         p2-ammo? false
         [[p1-move p2-move] & rest-turns] turns] ; Deconstruct the turns out

    (let [killed? (fn [m m2 a] (and a (= m \f) (not= m2 \g))) ; Function that checks if one player killed the other
          has-ammo? (fn [m a] (and (not= m \f) (or a (= m \r)))) ; Function that decides if a player has ammo in the
                                                                 ;  next turn
          p1-killed? (killed? p2-move p1-move p2-ammo?) ; Check if each player was killed.
          p2-killed? (killed? p1-move p2-move p1-ammo?)]

      (cond ; Check who (if any) died. If no one died, recur to next turn.
        (and p1-killed? p2-killed?) 0
        p1-killed? 2
        p2-killed? 1
        :else (recur (has-ammo? p1-move p1-ammo?)
                     (has-ammo? p2-move p2-ammo?)
                     rest-turns)))))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.