Visualize a Nim board like an expert
Python 2, 150 196 206 bytes
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
Try it online!
Ruby, 169 164 148 bytes
->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}}
Try it online!
First, we initialize
- the nim-sum with
s=eval a*?^
(which is shorter thana.reduce:^
) - the variable
c
, which stores the first unused unique character - a map
m
that maps power-of-two lengths to characters used to represent them
Then, looping over each pile, we run the following:
z=x-(x^s);[$><<?-*z,x-=z,s=0]if z>0
Per Wikipedia's strategy, if nim-sum XOR pile is less than pile, we should remove stones from that pile such that its length becomes nim-sum XOR pile. By storing the difference in the variable z
, we can test to see whether this difference is positive, and if so 1.) print that many dashes, 2.) subtract it from the pile, and 3.) set the nim-sum variable to zero to prevent further stone removal.
n=1
eval'[...];n*=2;x/=2;'*x
Now we "loop" over each bit and keep track of their values by repeatedly dividing x
by 2
and multiplying the accumulator n
by 2
. The loop is actually a string evaluated x
times, which is far greater than the log2(x)
times it's necessary, but no harm is done (aside from inefficiency). For each bit, we run the following if the bit is 1 (x&1>0
):
$><<(m[n]||c.next!)*n
Print a character n
times. If we already printed an unpaired group of this many stones, use that character; otherwise, use the next unused character (advancing c
in-place due to the !
).
m[n]=!m[n]&&c*1
If m[n]
existed (i.e. we just completed a pair), then m[n]
is reset. Otherwise, we just started a new pair, so set m[n]
to the character we used (*1
is a short way to make a copy of c
).