Visualisasikan papan Nim seperti seorang ahli

10

Latar Belakang

Dalam permainan Nim , pemain secara bergantian menghilangkan "batu" dari "tumpukan": di setiap belokan, pemain harus melepas di antara satu dan semua batu dari satu tumpukan. Tujuan Nim adalah untuk mengambil batu terakhir atau, dalam varian misere, untuk memaksa lawan Anda melakukannya - namun, ternyata strateginya hampir identik.

Nim membuat permainan bar yang menyenangkan. Anda dapat menggunakan korek api atau koin untuk "batu," dan "tumpukan" biasanya disusun dalam garis. Di bawah ini adalah pengaturan klasik dengan tumpukan 1, 3, 5, dan 7:

nim dengan korek api

Jika Anda belum pernah bermain Nim sebelumnya, Anda dapat mencobanya sebelum mencoba tantangan ini. Inilah versi yang disebut "Mutiara Sebelum Babi" .

Strategi

Strategi optimal dalam Nim cukup rumit sehingga kebanyakan orang awam kehilangan secara konsisten oleh seorang ahli, tetapi sederhana untuk digambarkan dengan aritmatika biner .

Melakukan operasi biner XOR mental, bagaimanapun, itu sulit, jadi untungnya ada cara yang setara untuk memvisualisasikan strategi yang benar yang lebih mudah untuk diterapkan secara real time, bahkan ketika mabuk.

Hanya ada tiga langkah:

  1. Secara mental kelompok "batu" di setiap baris ke dalam subkelompok yang ukurannya adalah kekuatan 2, dimulai dengan ukuran terbesar yang mungkin: 8, 4, 2, dan 1 cukup untuk sebagian besar permainan.
  2. Cobalah untuk mencocokkan setiap kelompok dengan kembaran di baris lain, sehingga setiap kelompok memiliki pasangan.
  3. Jika ini tidak memungkinkan, hapus "batu" tidak berpasangan dari satu baris (ini akan selalu mungkin - lihat tautan Wikipedia untuk alasannya) sehingga langkah 2. menjadi mungkin.

Atau, katakan dengan cara lain: "Keluarkan beberapa batu dari satu tumpukan sehingga jika Anda kemudian mengelompokkan tumpukan menjadi kekuatan 2 semua kelompok dapat dipasangkan dengan grup di tumpukan lain." Dengan peringatan bahwa Anda tidak dapat memecah kekuatan yang lebih besar dari 2 menjadi yang lebih kecil - misalnya, Anda tidak dapat mengelompokkan garis dengan 8 batu menjadi dua kelompok yang terdiri dari 4.

Misalnya, inilah cara Anda memvisualisasikan papan di atas:

batang korek api seimbang

Papan ini seimbang sempurna, jadi Anda ingin lawan Anda bergerak terlebih dahulu.

Tantangan

Diberikan daftar bilangan bulat positif yang mewakili ukuran "tumpukan" Nim, kembalikan visualisasi teks polos papan Nim seperti yang dilihat oleh seorang ahli.

Apa yang merupakan visualisasi yang valid paling baik dijelaskan dengan contoh, tetapi Anda harus:

  1. Tetapkan karakter yang berbeda untuk setiap "subkelompok kekuatan-2" dan pasangannya (subkelompok tidak berpasangan tidak memenuhi syarat), dan gunakan karakter itu untuk mewakili "batu" di kedua subkelompok dan pasangan.
  2. Mewakili setiap berpasangan "batu" (yaitu, orang-orang ahli akan menghapus ketika bermain yang normal - tidak misere - Nim) menggunakan tanda hubung: -.

Akan ada banyak cara untuk mencapai visualisasi yang valid, dan semuanya valid. Mari kita bahas beberapa kasus uji:

Uji Kasus

Input: 1, 3, 5, 7

Kemungkinan Output 1:

A
BBA
CCCCD
CCCCBBD

Anda dapat secara opsional memasukkan spasi di antara karakter, serta garis kosong di antara baris:

Kemungkinan Output 2:

A

B B A

C C C C D

C C C C B B D

Input: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Urutan dan pilihan karakter dapat apa saja yang Anda suka:

Kemungkinan Output 1:

G
E E
E E G
C C C C
C C C C F
B B B B D D
B B B B D D F
H H I - - - - -
A A A A A A A A I
A A A A A A A A H H

Simbol Unicode juga ok:

Kemungkinan Output 2:

◎
◈  ◈
◈  ◈  ◎
△  △  △  △
△  △  △  △  ◉
◐  ◐  ◐  ◐  ◀  ◀
◐  ◐  ◐  ◐  ◀  ◀  ◉
▽  ▽  ◒  -  -  -  -  -
▥  ▥  ▥  ▥  ▥  ▥  ▥  ▥  ◒ 
▥  ▥  ▥  ▥  ▥  ▥  ▥  ▥  ▽  ▽  

Input: 7

Dari aturan berikut bahwa "tumpukan tunggal" harus dihapus sepenuhnya.

Kemungkinan Output 1:

-------

Kemungkinan Output 2:

- - - - - - -

Input: 5, 5

Kemungkinan Output:

A A A A B
A A A A B

Aturan tambahan

  • Ini adalah kode golf dengan aturan standar. Kode terpendek menang.
  • Input fleksibel, dan dapat diambil dalam bentuk list-ish apa pun yang nyaman bagi Anda.
  • Keluaran juga fleksibel, seperti contoh di atas. Variasi paling masuk akal akan diizinkan. Tanyakan apakah Anda tidak yakin tentang sesuatu.
Jonah
sumber
1
Apakah ada batasan untuk berapa banyak batu yang bisa berisi setiap tumpukan, atau berapa banyak karakter berbeda yang diperlukan untuk visualisasi? (Dalam kasus ekstrem, bagaimana jika, misalnya, diperlukan lebih dari jumlah karakter ASCII yang dapat dicetak, atau lebih dari 255 karakter yang berbeda?)
Gagang Pintu
@ Doorknob Anda dapat menganggap itu tidak akan terjadi. Anda bahkan bisa berasumsi huruf-huruf alfabet akan cukup untuk input apa pun.
Jonah
@Jonah apakah ini akan menjadi output yang valid untuk test case kedua? ["H","EE","EEH","CCCC","CCCCI","DDDDFF","DDDDFFI","AAAAAAAA","AAAAAAAA-","----------"]
ngn
1
@ Οurous Saya kira jawabannya sederhana ya. Secara teknis AAAABBBBsebenarnya tidak valid, dan ABBtidak - tetapi itu membuat output kurang dapat dibaca jadi saya pikir hanya membuat penurunan dalam garis aturan eksplisit adalah yang terbaik.
Jonah
1
@ JonathanAllan Ya, saya mengandalkan logika bahwa ketiga langkah harus terjadi secara bersamaan. Jadi jika Anda melakukan langkah 1 dan 2 tetapi tidak dapat melakukan langkah 3, Anda harus menyesuaikan solusi Anda ke langkah 1 dan 2. Yang saya lihat membingungkan. Saya telah menambahkan deskripsi Anda di bawah ini.
Jonah

Jawaban:

2

Ruby, 169 164 148 byte

->a{s=eval a*?^
c=?@
m={}
a.map{|x|z=x-(x^s);[$><<?-*z,x-=z,s=0]if z>0
n=1
eval'x&1>0?[$><<(m[n]||c.next!)*n,m[n]=!m[n]&&c*1]:0;n*=2;x/=2;'*x
puts}}

Cobalah online!

Pertama, kita menginisialisasi

  • nim-sum dengan s=eval a*?^(yang lebih pendek dari a.reduce:^)
  • variabel c, yang menyimpan karakter unik pertama yang tidak digunakan
  • peta myang memetakan kekuatan dua hingga panjang karakter yang digunakan untuk mewakili mereka

Kemudian, mengulangi setiap tumpukan, kami menjalankan yang berikut:

z=x-(x^s);[$><<?-*z,x-=z,s=0]if z>0

Menurut strategi Wikipedia , jika tumpukan XOR nim-sum kurang dari tumpukan , kita harus menghilangkan batu dari tumpukan tersebut sehingga panjangnya menjadi tumpukan XOR nim-sum . Dengan menyimpan perbedaan dalam variabel z, kita dapat menguji untuk melihat apakah perbedaan ini positif, dan jika demikian 1.) mencetak banyak tanda hubung, 2.) kurangi dari tumpukan, dan 3.) atur variabel nim-sum ke nol untuk mencegah pemindahan batu lebih lanjut.

n=1
eval'[...];n*=2;x/=2;'*x

Sekarang kita "loop" pada setiap bit dan melacak nilainya dengan berulang kali membagi xdengan 2dan mengalikan akumulator ndengan 2. Loop sebenarnya adalah waktu yang dievaluasi string x, yang jauh lebih besar dari log2(x)waktu yang diperlukan, tetapi tidak ada salahnya dilakukan (selain dari inefisiensi). Untuk setiap bit, kami menjalankan yang berikut jika bitnya 1 ( x&1>0):

$><<(m[n]||c.next!)*n

Cetak satu nkali karakter . Jika kita sudah mencetak kelompok yang tidak berpasangan dari banyak batu ini, gunakan karakter itu; jika tidak, gunakan karakter yang tidak digunakan berikutnya (maju cdi tempat karena !).

m[n]=!m[n]&&c*1

Jika m[n]ada (yaitu kami baru saja menyelesaikan pasangan), maka m[n]diatur ulang. Jika tidak, kami baru saja memulai pasangan baru, jadi setel m[n]ke karakter yang kami gunakan ( *1adalah cara singkat untuk membuat salinan c).

Gagang pintu
sumber
4

Python 2 , 150 196 206 byte

def f(p):
 c=48;s=[l*'.'for l in p];m=2**len(bin(l))
 while m:
  if sum(m*'.'in l for l in s)>1:
   for i,l in enumerate(s):s[i]=l.replace('.'*m,chr(c)*m,`s`.count(chr(c)*m)<2)
   c+=1
  else:m/=2
 return s

Cobalah online!

TFeld
sumber
Saya pikir ini tidak berhasil 4, 9, 10.
Neil
@Neil Tangkapan bagus, harus diperbaiki sekarang
TFeld
1
Maaf, saya berhasil menipu lagi, kali ini dengan 14, 21, 35.
Neil
Itu juga gagal untuk [1, 3, 4, 5], di mana ia harus menghapus seluruh tumpukan kedua.
Gagang Pintu
@ Doorknob, Apakah itu diperlukan? Aturan mengatakanThere will be multiple ways to achieve a valid visualization, and all are valid
TFeld
1

JavaScript (ES6), 215 byte

f=
(a,c=0,x=eval(a.join`^`),i=a.findIndex(e=>(e^x)<e),b=a.map(_=>``),g=e=>(d=e&-e)&&a.map((e,i)=>e&d&&(a[i]-=d,b[i]=(c++>>1).toString(36).repeat(d)+b[i]))&&g(e-d))=>g(eval(a.join`|`),b[i]='-'.repeat(a[i]-(a[i]^=x)))||b
<textarea oninput=o.textContent=/\d/.test(this.value)?f(this.value.match(/\d+/g)).join`\n`:``></textarea><pre id=o>

Hanya memvisualisasikan hingga 36 karakter yang berbeda. Aku lega ini berhasil 1, 3, 4, 5.

Neil
sumber
Benar-benar bagus. Sangat menyenangkan untuk bermain dengannya dalam waktu nyata.
Jonah
1

Bersih , 454 byte

masih bermain golf

import StdEnv,Text,Data.List
$p=join"\n"[{#toChar c+'-'\\c<-e}\\e<-[take i(e++[0,0..])\\e<-r[[~c\\c<-reverse e,_<-[1..c]]\\e<-hd[q\\q<-foldr(\h t=[[a:b]\\a<-h,b<-t])[[]][[c\\c<-subsequences(takeWhile((>=)k)(iterate((*)2)1))|sum c<=k]\\k<-p]|sum[1\\a<-q&b<-p|sum a<>b]<2&&foldr(bitxor)0(flatten q)==0]]1&i<-p]]
r[]_=[]
r[h:t]n|all((<)0)h=[h:r t n]
#[m:_]=removeDup[e\\e<-h|e<0]
#(a,[x:b])=span(all((<>)m))t
=r([[if(e==m)n e\\e<-k]\\k<-[h:a]++[x]]++b)(n+1)

Cobalah online!

Menentukan fungsi $ :: [Int] -> String, mengambil ukuran tumpukan dan mengembalikan string di mana -menunjukkan batu yang akan dihapus, dan kelompok diwakili oleh karakter ASCII yang naik dari -. Jika diperlukan cukup grup, karakter akan membungkus kembali pada akhirnya, dan karena foldritu membutuhkan lebih dari satu gigabyte memori untuk menjalankan test-case kedua.

Versi indentasi dari pemahaman raksasa:

$p=join"\n"[
    {#
        toChar c+'-'
        \\c<-j
    }
    \\j<-[
        take i(e++[0,0..])
        \\e<-r[
            [
                ~c
                \\c<-reverse e
                ,_<-[1..c]
            ]
            \\e<-hd[
                q
                \\q<-foldr(\h t=[
                    [a:b]
                    \\a<-h
                    ,b<-t
                ])[[]][
                    [
                        c
                        \\c<-subsequences(takeWhile((>=)k)(iterate((*)2)1))
                        |sum c<=k
                    ]
                    \\k<-p
                ]
                |sum[
                    1
                    \\a<-q
                    &b<-p
                    |sum a<>b
                ]<2&&foldr(bitxor)0(flatten q)==0
            ]
        ]1
        &i<-p
    ]
]
Suram
sumber
Hanya ingin tahu, Bersih sepertinya mirip dengan haskell ... apa kelebihannya dibandingkan Haskell?
Jonah
1
@ Jonah Ini sangat mirip, ya. Dari atas kepala saya, ia memiliki manipulasi tipe tingkat rendah, IL / Majelis inline, dan interop dengan C semua dicapai lebih mudah daripada dengan Haskell. Namun, untuk penggunaan aktual, karena ketidakjelasan dan sifat eksperimental / akademik dari Clean, saya akan merekomendasikan Haskell (yang juga memiliki lebih banyak dukungan dalam hal perpustakaan, dan informasi referensi). Aku hanya suka Bersih itu saja.
Οairous