Python 2 3, 141-15 = 126
def win(x,y):w([y]*x)
w=lambda b,f=print:not[f(r+1,c+1)for r,p in enumerate(b)for c in range(p)if(r+c)*w(b[:r]+[min(i,c)for i in b[r:]],max)]
Ricerca minimax forza bruta. Per ogni possibile mossa, vediamo ricorsivamente se l'avversario può vincere dopo aver fatto quella mossa. Abbastanza debolmente golfato; qualcun altro dovrebbe essere in grado di fare molto meglio. Sembra un lavoro per APL.
winè l'interfaccia pubblica. Prende le dimensioni della scheda, la converte in una rappresentazione della scheda e la passa a w.
wè l'algoritmo minimax. Prende uno stato di board, prova tutte le mosse, crea un elenco i cui elementi corrispondono alle mosse vincenti e restituisce True se l'elenco è vuoto. Con l'impostazione predefinita f=print, la creazione dell'elenco ha un effetto collaterale nella stampa delle mosse vincenti. Il nome della funzione usato aveva più senso quando restituiva un elenco di mosse vincenti, ma poi ho spostato il notdavanti all'elenco per salvare uno spazio.
for r,p in enumerate(b)for c in xrange(p) if(r+c): Scorrere su tutte le mosse possibili. 1 1viene considerato non una mossa legale, semplificando un po 'il caso di base.
b[:r]+[min(i,c)for i in b[r:]]: Costruisci lo stato del tabellone dopo lo spostamento rappresentato da coordinate re c.
w(b[:r]+[min(i,c)for i in b[r:]],max): Ricerchi per vedere se il nuovo stato è in perdita. maxè la funzione più breve che ho trovato che richiederebbe due argomenti interi e non si lamenterebbe.
f(r+1,c+1): Se fè stampa, stampa lo spostamento. Qualunque cosa fsia, produce un valore per riempire la lunghezza dell'elenco.
not [...]: notritorna Trueper liste vuote e Falsenon vuote .
Codice Python 2 originale, completamente non modificato, inclusa la memoizzazione per gestire input molto più grandi:
def win(x, y):
for row, column in _win(Board([y]*x)):
print row+1, column+1
class MemoDict(dict):
def __init__(self, func):
self.memofunc = func
def __missing__(self, key):
self[key] = retval = self.memofunc(key)
return retval
def memoize(func):
return MemoDict(func).__getitem__
def _normalize(state):
state = tuple(state)
if 0 in state:
state = state[:state.index(0)]
return state
class Board(object):
def __init__(self, state):
self.state = _normalize(state)
def __eq__(self, other):
if not isinstance(other, Board):
return NotImplemented
return self.state == other.state
def __hash__(self):
return hash(self.state)
def after(self, move):
row, column = move
newstate = list(self.state)
for i in xrange(row, len(newstate)):
newstate[i] = min(newstate[i], column)
return Board(newstate)
def moves(self):
for row, pieces in enumerate(self.state):
for column in xrange(pieces):
if (row, column) != (0, 0):
yield row, column
def lost(self):
return self.state == (1,)
@memoize
def _win(board):
return [move for move in board.moves() if not _win(board.after(move))]
demo:
>>> for i in xrange(7, 11):
... for j in xrange(7, 11):
... print 'Dimensions: {} by {}'.format(i, j)
... win(i, j)
...
Dimensions: 7 by 7
2 2
Dimensions: 7 by 8
3 3
Dimensions: 7 by 9
3 4
Dimensions: 7 by 10
2 3
Dimensions: 8 by 7
3 3
Dimensions: 8 by 8
2 2
Dimensions: 8 by 9
6 7
Dimensions: 8 by 10
4 9
5 6
Dimensions: 9 by 7
4 3
Dimensions: 9 by 8
7 6
Dimensions: 9 by 9
2 2
Dimensions: 9 by 10
7 8
9 5
Dimensions: 10 by 7
3 2
Dimensions: 10 by 8
6 5
9 4
Dimensions: 10 by 9
5 9
8 7
Dimensions: 10 by 10
2 2