Hình dung một bảng Nim như một chuyên gia


10

Lý lịch

Trong trò chơi của Nim , người chơi luân phiên tháo "đá" từ "đống": trên mỗi lượt, người chơi phải loại bỏ từ một đến tất cả các đá từ một đơn cọc. Mục tiêu của Nim là lấy viên đá cuối cùng hoặc, trong biến thể sai lầm, buộc đối thủ của bạn phải làm như vậy - tuy nhiên, hóa ra các chiến lược gần như giống hệt nhau.

Nim làm một trò chơi thanh vui vẻ. Bạn có thể sử dụng que diêm hoặc tiền xu cho "đá" và "cọc" thường được sắp xếp thành một hàng. Dưới đây là một thiết lập cổ điển với các cọc 1, 3, 5 và 7:

nim với que diêm

Nếu bạn chưa từng chơi Nim trước đây, bạn có thể thử sức mình trước khi thử thách này. Đây là một phiên bản gọi là "Ngọc trai trước khi lợn" .

Chiến lược

Chiến lược tối ưu trong Nim đủ phức tạp để hầu hết giáo dân thường thua một chuyên gia, nhưng đơn giản để mô tả với số học nhị phân .

Tuy nhiên, thực hiện các thao tác XOR nhị phân tinh thần rất khó khăn, vì vậy may mắn thay, có một cách tương đương để hình dung chiến lược chính xác, dễ thực hiện hơn trong thời gian thực, ngay cả khi say rượu.

Chỉ có ba bước:

  1. Nhóm các "viên đá" trong mỗi dòng thành các nhóm nhỏ có kích thước là 2, bắt đầu với kích thước lớn nhất có thể: 8, 4, 2 và 1 là đủ cho hầu hết các trò chơi.
  2. Cố gắng ghép từng nhóm với một cặp trong một dòng khác, để mỗi nhóm có một cặp.
  3. Nếu điều này là không thể, hãy loại bỏ các "viên đá" không ghép đôi khỏi một dòng duy nhất (điều này sẽ luôn luôn có thể - xem liên kết Wikipedia để biết lý do) để bước 2. trở nên khả thi.

Hoặc, nói một cách khác: "Loại bỏ một số viên đá khỏi một cọc sao cho nếu sau đó bạn nhóm các cọc thành sức mạnh của 2 thì tất cả các nhóm có thể được ghép với một nhóm trong một số cọc khác." Với lời cảnh báo rằng bạn không thể chia sức mạnh lớn hơn của 2 thành các nhóm nhỏ hơn - ví dụ: bạn không thể nhóm một dòng có 8 viên đá thành hai nhóm 4.

Ví dụ: đây là cách bạn hình dung bảng ở trên:

que diêm cân bằng

Bảng này hoàn toàn cân bằng, vì vậy bạn muốn đối thủ của mình di chuyển trước.

Các thách thức

Đưa ra một danh sách các số nguyên dương biểu thị kích thước của "cọc" Nim, trả về một trực quan văn bản đơn giản của bảng Nim như một chuyên gia nhìn thấy.

Những gì cấu thành một trực quan hợp lệ được giải thích tốt nhất bằng ví dụ, nhưng bạn phải:

  1. Gán một ký tự riêng cho từng "nhóm sức mạnh 2" và cặp của nó (các nhóm con chưa ghép đôi không đủ điều kiện) và sử dụng ký tự đó để biểu thị các "viên đá" trong cả hai nhóm và cặp.
  2. Đại diện cho bất kỳ "viên đá" chưa ghép nối nào (nghĩa là những viên đá mà một chuyên gia sẽ loại bỏ khi chơi bình thường - không phải là sai - Nim) bằng cách sử dụng dấu gạch nối : -.

Sẽ có nhiều cách để đạt được một hình dung hợp lệ, và tất cả đều hợp lệ. Chúng ta hãy làm việc thông qua một số trường hợp thử nghiệm:

Các trường hợp thử nghiệm

Đầu vào: 1, 3, 5, 7

Đầu ra có thể 1:

A
BBA
CCCCD
CCCCBBD

Bạn có thể tùy ý bao gồm khoảng trắng giữa các ký tự, cũng như các dòng trống giữa các hàng:

Đầu ra có thể 2:

A

B B A

C C C C D

C C C C B B D

Đầu vào: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Thứ tự và sự lựa chọn của các nhân vật có thể là bất cứ điều gì bạn thích:

Đầu ra có thể 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

Các ký hiệu Unicode cũng ok:

Đầu ra có thể 2:

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

Đầu vào: 7

Từ các quy tắc, theo đó bất kỳ "cọc đơn" nào cũng phải được loại bỏ hoàn toàn.

Đầu ra có thể 1:

-------

Đầu ra có thể 2:

- - - - - - -

Đầu vào: 5, 5

Đầu ra có thể:

A A A A B
A A A A B

Quy tắc bổ sung

  • Đây là mã golf với các quy tắc tiêu chuẩn. Mã ngắn nhất sẽ thắng.
  • Đầu vào là linh hoạt và có thể được thực hiện dưới bất kỳ hình thức danh sách nào thuận tiện cho bạn.
  • Đầu ra cũng linh hoạt, như các ví dụ trên minh họa. Các biến thể hợp lý nhất sẽ được cho phép. Hỏi nếu bạn không chắc chắn về điều gì đó.

1
Có giới hạn về số lượng mỗi viên đá có thể chứa bao nhiêu, hoặc có bao nhiêu ký tự riêng biệt sẽ cần thiết cho hình dung? (Trong trường hợp cực đoan, ví dụ, nếu cần nhiều hơn số lượng ký tự ASCII có thể in được, hoặc hơn 255 ký tự riêng biệt thì sao?)
Doorknob

@Doorknob Bạn có thể cho rằng điều đó sẽ không xảy ra. Bạn thậm chí có thể cho rằng các chữ cái trong bảng chữ cái sẽ đủ cho bất kỳ đầu vào nào.
Giô-na

@Jonah đây có phải là đầu ra hợp lệ cho trường hợp thử nghiệm thứ hai không? ["H","EE","EEH","CCCC","CCCCI","DDDDFF","DDDDFFI","AAAAAAAA","AAAAAAAA-","----------"]
ngn

1
@ Urous Tôi nghĩ câu trả lời đơn giản là có. Về mặt kỹ thuật AAAABBBBthực sự không hợp lệ, và ABBkhông phải - nhưng nó làm cho đầu ra ít đọc hơn nên tôi nghĩ chỉ cần giảm trong một dòng là một quy tắc rõ ràng là tốt nhất.
Giô-na

1
@Jonathan ALLan Vâng, tôi đang dựa vào logic rằng cả 3 bước phải xảy ra đồng thời. Vì vậy, nếu bạn thực hiện bước 1 và 2 nhưng không thể thực hiện bước 3, bạn phải điều chỉnh giải pháp của mình sang bước 1 và 2. Điều mà tôi có thể thấy là khó hiểu. Tôi đã thêm mô tả của bạn dưới đây.
Giô-na

Câu trả lời:


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}}

Hãy thử trực tuyến!

Đầu tiên, chúng tôi khởi tạo

  • nim-sum với s=eval a*?^(ngắn hơn a.reduce:^)
  • biến c, lưu trữ ký tự duy nhất không sử dụng đầu tiên
  • một bản đồ mánh xạ hai chiều dài thành các ký tự được sử dụng để thể hiện chúng

Sau đó, lặp qua từng cọc, chúng tôi chạy như sau:

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

Theo chiến lược của Wikipedia , nếu cọc XOR nim-sum nhỏ hơn cọc , chúng ta nên loại bỏ đá khỏi cọc đó sao cho chiều dài của nó trở thành cọc XOR nim-sum . Bằng cách lưu trữ sự khác biệt trong biến z, chúng ta có thể kiểm tra xem sự khác biệt này có dương hay không và nếu có 1.) in nhiều dấu gạch ngang, 2.) trừ nó ra khỏi cọc và 3.) đặt biến nim-sum thành không để ngăn chặn thêm đá.

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

Bây giờ chúng ta "loop" trên mỗi bit và theo dõi các giá trị của họ bằng cách liên tục chia xbằng 2và nhân accumulator nbằng 2. Vòng lặp thực sự là một chuỗi xthời gian được đánh giá , lớn hơn nhiều log2(x)lần so với số lần cần thiết, nhưng không có tác hại nào được thực hiện (ngoài hiệu quả). Đối với mỗi bit, chúng tôi chạy như sau nếu bit là 1 ( x&1>0):

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

In một nlần ký tự . Nếu chúng ta đã in một nhóm không ghép đôi của nhiều viên đá này, hãy sử dụng ký tự đó; mặt khác, sử dụng ký tự không sử dụng tiếp theo (tiến ctại chỗ do !).

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

Nếu m[n]tồn tại (tức là chúng ta vừa hoàn thành một cặp), sau đó m[n]được đặt lại. Mặt khác, chúng tôi chỉ bắt đầu một cặp mới, vì vậy hãy đặt thành m[n]ký tự mà chúng tôi đã sử dụng ( *1là một cách ngắn để tạo một bản sao c).


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

Hãy thử trực tuyến!


Tôi không nghĩ rằng điều này làm việc cho 4, 9, 10.
Neil

@Neil Bắt tốt, nên được sửa ngay bây giờ
TFeld

1
Xin lỗi, tôi quản lý để đánh lừa nó một lần nữa, lần này với 14, 21, 35.
Neil

Nó cũng thất bại trong [1, 3, 4, 5], trong đó cần loại bỏ toàn bộ cọc thứ hai.
Doorknob

@Doorknob, Có bắt buộc không? Các quy tắc nóiThere 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>

Chỉ hiển thị tối đa 36 nhân vật khác nhau. Tôi cảm thấy nhẹ nhõm vì công việc này 1, 3, 4, 5.


Thực sự tốt đẹp. Thật vui khi chơi với nó trong thời gian thực.
Giô-na

1

Sạch , 454 byte

vẫn chơi 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)

Hãy thử trực tuyến!

Xác định hàm $ :: [Int] -> String, lấy kích thước cọc và trả về một chuỗi trong đó -biểu thị các viên đá được loại bỏ và các nhóm được biểu thị bằng các ký tự ASCII tăng dần từ đó -. Nếu đủ các nhóm là cần thiết, các nhân vật cuối cùng sẽ quay lại, và do foldrnó đòi hỏi nhiều hơn một gigabyte bộ nhớ để chạy trường hợp thử nghiệm thứ hai.

Phiên bản thụt lề của sự hiểu biết khổng lồ:

$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
    ]
]

Chỉ tò mò, Clean có vẻ tương tự như haskell ... lợi thế của nó so với Haskell là gì?
Giô-na

1
@Jonah Nó khá giống nhau, đúng vậy. Ngoài đỉnh đầu của tôi, nó có thao tác loại cấp thấp hơn, IL / hội đồng nội tuyến và giao tiếp với C đều đạt được dễ dàng hơn so với Haskell. Tuy nhiên, để sử dụng thực tế, do tính tối nghĩa và thử nghiệm / học thuật của Clean, tôi muốn giới thiệu Haskell (cũng có nhiều hỗ trợ hơn về mặt thư viện và thông tin tham khảo). Tôi chỉ thích Clean là tất cả.
urous
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.