Bisakah Anda menang dengan dua gerakan lagi di Three Men's Morris?

16

Bounties

1 ( diberikan )

Saya akan memberikan 50 rep untuk jawaban pertama yang valid

2 ( diberikan )

Saya akan memberikan 100 rep lagi untuk jawaban valid terpendek.

3 ( terbuka untuk pengiriman )

Saya akan memberikan 200 rep untuk yang pertama dengan jawaban valid pendek yang signifikan. Signifikan paling banyak 45% dari jawaban terpendek saat ini ( 564 byte x 0,45 = maks 254 byte ).


Permainan

Anda ingat permainan klasik " Nine Men's Morris " atau sekadar " Mill "? Ada variasi yang disebut Three Men's Morris yang sedikit mirip dengan tic-tac-toe yang bisa berubah.

Aturan

Ini adalah papan permainan kosong:

   a   b   c
1 [ ]–[ ]–[ ]
   | \ | / |
2 [ ]–[ ]–[ ]
   | / | \ |
3 [ ]–[ ]–[ ]

[ ]adalah bidang dan |–/\mewakili rute antara bidang tersebut.

Permainan ini dimainkan oleh dua pemain 1dan 2yang masing-masing menempatkan 3 token di papan tulis. Ini sebenarnya sudah terjadi dan kita ada di dalam game. Permainan dimenangkan jika satu pemain dapat membentuk millbaris vertikal atau horizontal dari 3 token pemain.

Token dapat dipindahkan di papan sepanjang garis penghubung, sesuai dengan aturan ini:

Untuk setiap posisi kosong yang berdekatan (yaitu dari posisi tepi ke tengah, atau dari pusat ke posisi tepi, atau dari posisi tepi ke posisi tepi yang berdekatan)

Seorang pemain harus bergerak kecuali tidak ada posisi kosong yang berdekatan, dalam hal ini lompatan dilewati.

Tantangan

Anda pemain 1dan gerakan Anda berikutnya. Tulis program atau fungsi, yang menentukan apakah:

  • Anda bisa memaksakan kemenangan dengan 2 gerakan atau kurang ( kemenangan yang pasti )
  • Anda bisa menang dengan 2 gerakan atau kurang, jika lawan membuat kesalahan ( kemungkinan menang )
  • Anda tidak dapat menang dengan 2 gerakan atau kurang, karena Anda akan membutuhkan lebih banyak gerakan atau karena gerakan paksa membuat lawan Anda menang ( mustahil untuk menang )

Persyaratan

  • Meskipun Anda pasti menang ketika Anda membuat lawan Anda mati, program Anda harus selesai dalam waktu yang terbatas.
  • Anda dapat menulis suatu program atau fungsi.

Memasukkan

Para pemain diwakili oleh 1dan 2. 0mendefinisikan bidang gratis. Anda dapat mengambil input sebagai matriks atau array.

Pasti

A         B         C         D
2 1 0  |  2 1 0  |  1 0 1  |  1 2 2
2 1 2  |  0 1 0  |  1 0 2  |  2 1 O
0 0 1  |  2 2 1  |  0 2 2  |  O O 1

A: [2,1,0,2,1,2,0,0,1]
B: [2,1,0,0,1,0,2,2,1]
C: [1,0,1,1,0,2,0,2,2]
D: [1,2,2,2,1,0,0,0,1]

Bisa jadi

A         B         C
1 0 1  |  1 0 1  |  1 2 2
1 2 2  |  1 2 0  |  0 0 1
2 0 0  |  2 0 2  |  2 1 0

A: [1,0,1,1,2,2,2,0,0]
B: [1,0,1,1,2,0,2,0,2]
C: [1,2,2,0,0,1,2,1,0]

Mustahil

A         B    
1 0 0  |  1 2 0
1 2 2  |  2 1 0
2 0 1  |  1 2 0

A: [1,0,0,1,2,2,2,0,1]
B: [1,2,0,2,1,0,1,2,0]

Keluaran

Program Anda harus menampilkan / mengembalikan smiley:

  • Kemenangan pasti: :)
  • Kemungkinan menang: :|
  • Tidak mungkin menang: :(

Contohnya

Kemenangan pasti dalam dua gerakan:

[2][1][ ] 1. [2][1][ ]
[2][1][2] -> [2][1][2]
[ ][ ][1]    [ ][1][ ]

[2][1][ ] 1. [2][1][ ]    [ ][1][ ] 2. [ ][ ][1]
[ ][1][ ] -> [ ][ ][1] -> [2][ ][1] -> [2][ ][1]
[2][2][1]    [2][2][1]    [2][2][1]    [2][2][1]

[1][ ][1] 1. [ ][1][1]    [ ][1][1] 2. [1][1][1]
[1][ ][2] -> [1][ ][2] -> [1][ ][2] -> [ ][ ][2]
[ ][2][2]    [ ][2][2]    [2][ ][2]    [2][ ][2]

Kemungkinan menang dalam dua gerakan:

[1][ ][1] 1. [ ][1][1]    [ ][1][1] 2. [1][1][1]
[1][2][ ] -> [1][2][ ] -> [1][2][2] -> [ ][2][2]
[2][ ][2]    [2][ ][2]    [2][ ][ ]    [2][ ][ ]

[1][ ][1] 1. [ ][1][1]    [ ][1][1] 2. [1][1][1]
[1][2][ ] -> [1][2][ ] -> [1][2][2] -> [ ][2][2]
[2][ ][2]    [2][ ][2]    [2][ ][ ]    [2][ ][ ]

[1][2][2] 1. [ ][2][2]    [2][ ][2] 2. [1][2][2]
[ ][ ][1] -> [1][ ][1] -> [1][ ][1] -> [1][1][1]
[2][1][ ]    [2][1][ ]    [2][1][ ]    [2][ ][ ]

Tidak mungkin menang dalam dua gerakan:

[1][ ][ ]
[1][2][2]
[2][ ][1]

Bonus

Jika kemenangan pasti dimungkinkan dan program Anda mengeluarkan gerakan dari satu cara menuju sukses juga seperti a1:a2(1 gerakan) atau a1:a2,a3:b2(2 gerakan), Anda dapat menarik 30% dari jumlah byte Anda.


Ini adalah kode golf - jadi jawaban tersingkat dalam byte menang. Celah standar tidak diijinkan.


Terima kasih kepada Peter Taylor yang memperbaiki beberapa kekurangan dan memperbaiki kata-kata di Sandbox .

masukkan nama pengguna di sini
sumber
Terkait .
masukkan nama pengguna di sini
1
Saya suka tabel / grafik = ascii itu)
flawr
1
Apa yang terjadi jika seorang pemain tidak dapat bergerak? mis. di [1,0,0,2,1,0,2,2,1], pemain 2 tidak bisa bergerak - apakah ini kemenangan untuk pemain 1?
VisualMelon
1
@LeifWillerts Saya mungkin salah paham apa yang Anda maksud, tetapi dalam hal ini, tidak ada pemain yang menang - mereka hanya menang dengan memiliki garis horizontal atau vertikal (bukan diagonal).
VisualMelon
3
Yah, hanya ada 1680 posisi dewan yang valid, sehingga hardcoding dapat memberikan sedikit lebih dari 210 byte. (lebih sedikit jika dipertimbangkan simetri)
lirtosiast

Jawaban:

1

Haskell, 580 564 441 byte

Sejauh ini saya bisa bermain golf untuk saat ini. Tidak yakin apakah bahasa lain dapat mengalahkannya.

Panggil mdaftar daftar seperti [[2,1,0],[2,1,2],[0,0,1]](Pasti A).

import Data.Array
r=[0..2]
p?f=[(x,y)|x<-r,y<-r,f!y!x==p]
p%f=all(==x)xs||all(==y)ys where(x:xs,y:ys)=unzip$p?f
s p x y f=f//[(y,f!y//[(x,p)])]
p#f=[s 0 x y$s p u v f|a@(x,y)<-p?f,b@(u,v)<-0?f,((x-u)*(y-v)==0&&abs(x+y-u-v)==1)||elem(1,1)[a,b]]
p&f|p#f>[]=p#f|0<1=[f]
e=any
i a p f=e(a$e(p%))(map(map(p&))(map((3-p)&)$p&f))||e(p%)(p&f)
l=listArray(0,2)
f(True,_)=":)"
f(False,True)=":|"
f _=":("
m=putStrLn.f.(\f->(i all 1 f,i e 1 f)).l.map l

Kode uji:

da = [[2,1,0],[2,1,2],[0,0,1]]
db = [[2,1,0],[0,1,0],[2,2,1]]
dc = [[1,0,1],[1,0,2],[0,2,2]]
dd = [[1,2,2],[2,1,0],[0,0,1]]
pa = [[1,0,1],[1,2,2],[2,0,0]]
pb = [[1,0,1],[1,2,0],[2,0,2]]
pc = [[1,2,2],[0,0,1],[2,1,0]]
ia = [[1,0,0],[1,2,2],[2,0,1]]
ib = [[1,2,0],[2,1,0],[1,2,0]]
al = [da,db,dc,dd,pa,pb,pc,ia,ib]

mapM_ m al pengembalian:

:)
:)
:)
:)
:|
:|
:|
:(
:(
Leif Willerts
sumber
1
Diperbaiki, saya pikir. Akan
menggandakan cek
5

C # - 739 663 byte

Program yang lengkap, membaca input dari argv, dan tampaknya berfungsi. Jalankan seperti

ThreeMill 1 2 1 1 2 0 0 0 2

Jika metode input ini tidak dapat diterima, saya akan senang mengubahnya (tidak pernah suka menggunakan argv).

using System;using System.Linq;class P{static void Main(string[]A){var I=new[]{0,3,6,1,4,7,2,5,8};Func<string[],string>J=S=>S[0]+S[1]+S[2]+" "+S[3]+S[4]+S[5]+" "+S[6]+S[7]+S[8]+" ";Func<string[],string,int>W=(B,p)=>(J(B)+J(I.Select(i=>B[i]).ToArray())).Contains(p+p+p)?1:0;Func<string[],string,string[][]>V=(B,p)=>I.SelectMany(a=>I.Where(b=>a!=b&B[a]==p&B[b]=="0"&(a==4|b==4|a-b==3|b-a==3|((a-b==1|b-a==1)&a/3==b/3))).Select(b=>{var N=(string[])B.Clone();N[b]=p;N[a]="0";return N;})).DefaultIfEmpty(B).ToArray();int h,G;Console.WriteLine(":"+"(|))"[V(A,"1").Max(z=>((h=0)<(G=V(z,"2").Sum(j=>V(j,"1").Max(q=>W(q,"1")-W(q,"2"))+h++*0))?1:0)+(h>G?W(z,"1")*2:2))]);}}

Saya enggan untuk memposting ini kemarin, karena saya belum bisa bermain golf banyak (tidak punya banyak waktu, dan saya mungkin keluar dari latihan), tetapi karena belum ada tanggapan, saya ' Akan mempostingnya, saya tentu tidak mengharapkan karunia, saya lebih suka itu pergi ke seseorang yang menaruh sedikit usaha lebih banyak ke mereka sebelum memposting!

Sunting: mengganti semua bools dengan ints, yang berarti saya bisa menggunakan Linq dengan lebih baik, dan berhasil menutup kedua loop foreach, memberikan penghematan besar. Saya sedikit kagum bahwa hpenghitung bekerja ... ++ adalah utilitas yang sangat halus.

Program ini sangat sederhana, hanya mengeksplorasi setiap set gerakan yang mungkin (menyimpan status board dalam string []). Itu mengulangi semua kemungkinan pergerakan kami (papan yang menghasilkannya), dan menghitung jumlah respons lawan kami yang dapat kami kalahkan dengan sukses (G ) (yaitu yang kami menangkan, dan ia tidak menang). Itu juga menghitung jumlah kemungkinan tanggapan (h). Jika kita bisa memenangkan apapun, maka itu mungkin, dan kita menambahkan 1 ke jumlah, jika kita bisa memenangkan semuanya, itu pasti, dan kami menambahkan 2 ke jumlah. Karena itu, beberapa maksimum adalah hasil terbaik kami, dan kami indeks ke string "(|))" untuk mengembalikan wajah yang sesuai. Perhatikan bahwa kita memerlukan tambahan ")" karena jumlahnya bisa 2 atau 3 jika itu pasti (mungkin saja kita tampaknya tidak dapat mengalahkan respons yang telah dimenangkan pada go pertama, jadi kemungkinan cek adalah sedikit menyesatkan).

Program memeriksa kemenangan dengan menghasilkan string dari papan, yaitu baris dan kolom yang dipisahkan ruang, dan hanya mencari string 3 karakter pemain dalam string ini (mis. "201 201 021 220 002 111" adalah menang untuk kita)

using System;
using System.Linq; // all important

class P
{
    static void Main(string[]A) // transform to int?
    {
        var I=new[]{0,3,6,1,4,7,2,5,8}; // vertical indexes
        Func<string[],string>J=S=>S[0]+S[1]+S[2]+" "+S[3]+S[4]+S[5]+" "+S[6]+S[7]+S[8]+" "; // joins the strings up, so that there is a space separating each group of three (including at end)
        Func<string[],string,int>W=(B,p)=>(J(B)+J(I.Select(i=>B[i]).ToArray())).Contains(p+p+p)?1:0; // checks if a particular player wins
        Func<string[],string,string[][]>V=(B,p)=>I.SelectMany(a=>I // for each imagineable move
            .Where(b=>a!=b&B[a]==p&B[b]=="0"&(a==4|b==4|a-b==3|b-a==3|((a-b==1|b-a==1)&a/3==b/3))) // where it's legal
            .Select(b=>{var N=(string[])B.Clone();N[b]=p;N[a]="0";return N;}) // select the resulting board
        ).DefaultIfEmpty(B) // allow not-moving
        .ToArray();

        int h, // h stores the number of responses the opponent has to each move
        G; // G stores the number of responses by the opponent we can beat

        Console.WriteLine(":"+"(|))"[ // we index into this to decide which smiley
            V(A,"1").Max(z=>
                    ((h=0)<(G=V(z,"2").Sum(j=>V(j,"1").Max(q=>W(q,"1")-W(q,"2"))+h++*0))?1:0) // if there is atleast 1 reponse by the opponent we can beat, we can possibly win
                    +(h>G?W(z,"1")*2:2) // if there are moves which we can't win, then if we have already won (one-move), else, we can definitely win
                   ) // sum is therefore 0 if impossible, 1 if possible, >2 (no more than 3) if definite 
            ]);

    }
}

Ini skrip pengujian saya:

ThreeMill 2 1 0 2 1 2 0 0 1
ThreeMill 2 1 0 0 1 0 2 2 1
ThreeMill 1 0 1 1 0 2 0 2 2
ThreeMill 1 2 2 2 1 0 0 0 1

ThreeMill 1 0 1 1 2 2 2 0 0
ThreeMill 1 0 1 1 2 0 2 0 2
ThreeMill 1 2 2 0 0 1 2 1 0

ThreeMill 1 0 0 1 2 2 2 0 1
ThreeMill 1 2 1 1 2 0 0 0 2
ThreeMill 1 0 1 2 0 2 1 0 2

Output yang mana

:)
:)
:)
:)
:|
:|
:|
:(
:|
:)
VisualMelon
sumber
Bagus. Terima kasih telah menjadi yang pertama. :) Jika tidak apa-apa, saya akan memberikan hadiah setelah akhir pekan, untuk menyimpannya beberapa hari lagi di tab unggulan.
masukkan nama pengguna di sini
@insertusernamehere Tidak apa-apa bagi saya, jika saya tidak dapat diganggu untuk melakukan pekerjaan nyata, saya mungkin akan melakukan beberapa pekerjaan lagi besok.
VisualMelon
1
Ini mengingatkan saya pada komentar ini: " Saya belum bisa menjawab pertanyaan untuk MENIT SAAT MENIT. Ini sangat penting! Kirim saja rincian basis data dan saya akan memasukkan jawaban SQL saya. Saya punya banyak pekerjaan yang harus dihindari dan tanpa alasan untuk menghindarinya !! "pada Mengapa Stack Overflow tidak berfungsi? . :)
insertusernamehere
1

PowerShell 576 550 byte

Saya tidak akan mudah dihalangi - jika saya tidak bisa mendapatkan C # di bawah 631 byte, saya harus menggunakan bahasa yang berbeda sebagai gantinya! Saya berharap bahwa Leif Willerts akan menjatuhkan 5 byte dari jawabannya, karena saya telah memutuskan saya tidak terlalu menyukai PowerShell, mungkin saya hanya perlu melihatnya secara objektif dalam hal jumlah byte ...

Ini adalah skrip, Anda menjalankannya . .\mill.ps1 "201102021". Salinan jawaban C # saya cukup bagus, hanya dalam bahasa yang tidak banyak saya alami. Saya belum berusaha terlalu keras untuk bermain golf ini, karena butuh waktu begitu lama untuk bisa bekerja pada contoh pertama, dan sudah cukup ringkas.

Sunting: tidak bisa meninggalkan [Math]::Floorpanggilan itu di sana

param($U);$I=0,3,6,1,4,7,2,5,8;function J($S){($S[0..2]+" "+$S[3..5]+" "+$S[6..8]-join"").Contains($p*3)}function W($D,$p){(J $D)-or(J $D[$I])}function V($Q,$C){$I|%{$a=$_;$I|?{$a-ne$_-and$Q[$a]-eq$c-and$Q[$_]-eq"0"-and($a-eq4-or$_-eq4-or$a-$_-eq3-or$_-$a-eq3-or(($a-$_-eq1-or$_-$a-eq1)-and$a/3-$a%3/3-eq$_/3-$_%3/3))}|%{$b=$Q[0..8];$b[$_]=$c;$b[$a]=0;$b-join''}}|%{$n=1}{$n=0;$_}{if($n){$Q}}}$e=$f=0;V $U "1"|%{$h=0;$x=$_;V $x "2"|%{$k=0;(V $_ "1"|%{if((W $_ "1")-and!(W $_ "2")){$k=$e=1}});$h+=1-$k};if($h-eq0-or(W $x "1")){$f=2}};":"+"(|))"[$e+$f]

Jika Anda deskripsi cara kerjanya ... jawaban C # adalah untuk Anda, tetapi mudah-mudahan komentar membuatnya cukup jelas. Titik koma mungkin tidak cocok dengan perintah single-line, saya belum yakin di mana mereka dibutuhkan dan tidak, dan tidak menyalinnya kembali ketika saya meletakkan semuanya pada satu baris.

param($U); # take input as argument

$I=0,3,6,1,4,7,2,5,8; # cols

function J($S){ # checks if this is a winning string
($S[0..2]+" "+$S[3..5]+" "+$S[6..8]-join"").Contains($p*3)}

function W($D,$p){ # checks if this is a winning board
(J $D)-or(J $D[$I])} # $D[$I] reorganises into columns

function V($Q,$C){ # yields all valid moves from position $Q for player $C
$I|%{$a=$_;$I| # for each possible move
?{$a-ne$_-and$Q[$a]-eq$c-and$Q[$_]-eq"0"-and($a-eq4-or$_-eq4-or$a-$_-eq3-or$_-$a-eq3-or(($a-$_-eq1-or$_-$a-eq1)-and$a/3-$a%3/3-eq$_/3-$_%3/3))}| # where legal
%{$b=$Q[0..8];$b[$_]=$c;$b[$a]=0;$b-join''}}| # make the move (copy $Q to an array, modify, join into a string)
%{$n=1}{$n=0;$_}{if($n){$Q}}} # if empty, return $Q - I am confident this can be achieved with commas, and [0], and maybe a +, but I don't want to think about it

$e=$f=0; # possible, definite

V $U "1"|%{ # for all our possible moves
$h=0;$x=$_; # $k is whether we win all of these
  V $x "2"| # for all opponent's responses
  %{$k=0;(V $_ "1"| # for all our responses
  %{if((W $_ "1")-and!(W $_ "2")){$k=$e=1}});$h+=1-$k}; # if we can win and he can't, then things are looking good, set $e to 1 (possible win)

  if($h-eq0-or(W $x "1")){$f=2} # if we win every move, or we have already won, it's a definite
};

":"+"(|))"[$e+$f] # smile, it's all over

Skrip uji (PowerShell):

. .\mill.ps1 "210212001"
. .\mill.ps1 "210010221"
. .\mill.ps1 "101102022"
. .\mill.ps1 "122210001"

. .\mill.ps1 "101122200"
. .\mill.ps1 "101120202"
. .\mill.ps1 "122001210"

. .\mill.ps1 "100122201"
. .\mill.ps1 "121120002"
. .\mill.ps1 "101202102"

. .\mill.ps1 "100122201"
. .\mill.ps1 "120210120"

Outputnya:

:)
:)
:)
:)
:|
:|
:|
:(
:|
:)
:(
:(
VisualMelon
sumber
1

Python 3, 566 557 byte

Saya harus melihat apakah saya bisa menurunkannya lebih lanjut, atau jika saya bisa mendapatkan bonus 30%, tetapi setelah banyak menunda, inilah jawaban saya.

def t(g,x=1,r=0,z=0):
 m=[[1,3,4],[0,2,4],[2,4,5],[0,4,6],[0,1,2,3,5,6,7,8],[2,4,8],[3,4,7],[4,6,8],[4,5,7]];a=[[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];z=z or[[],[],[],[]];s=0
 if r>3:return z
 for i in a:
  if g[i[0]]==g[i[1]]==g[i[2]]>0:s=g[i[0]];break
 z[r]+=s,
 for q in range(9):
  i=g[q]
  if i==x:
   for p in m[q]:
    if g[p]<1:n=g[:];n[q],n[p]=n[p],n[q];z=t(n,3-x,r+1,z)
 if r:return z
 else:
  w=l=0
  for j in range(4):w=w or 1in z[j];l=l or 2in z[j]
  if l<1and w:return":)"
  elif w<1and l:return":("
  else:return":|"

Tidak Terkumpul:

def three_mens_morris(grid, player=1, rec=0, w_l=0, p=0):
    moves = [[1,3,4],[0,2,4],[2,4,5],[0,4,6],[0,1,2,3,5,6,7,8],[2,4,8],[3,4,7],[4,6,8],[4,5,7]]
    w_l = w_l or [[],[],[],[]]
    if rec == 4: return w_l
    result = check_grid(grid)
    w_l[rec].append(result)
    for sq_1 in range(len(grid)):
        piece = grid[sq_1]
        if piece == player:
            for sq_2 in moves[sq_1]:
                if grid[sq_2] == 0:
                    new_grid = grid.copy()
                    new_grid[sq_1],new_grid[sq_2]=new_grid[sq_2],new_grid[sq_1]
                    w_l = three_mens_morris(new_grid,3-player,rec+1,w_l)
    if p: print(w_l)
    if rec:
        return w_l
    else:
        win = loss = 0
        for i in range(4):
            if 1 in w_l[i]:
                win = 1
            elif 2 in w_l[i]:
                loss = 1
        if p:print(win,loss)
        if loss==0 and win:
            return ":)"
        elif loss and win==0:
            return ":("
        else:
            return ":|"

def check_grid(grid):
    rows = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
    for i in rows:
        if grid[i[0]]==grid[i[1]]==grid[i[2]] and grid[i[0]]:
            return grid[i[0]]
    return 0
Sherlock9
sumber