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)]
Recherche minimax par force brute. Pour chaque coup possible, nous voyons récursivement si l'adversaire peut gagner après avoir fait ce coup. Assez faiblement golfé; quelqu'un d'autre devrait pouvoir faire beaucoup mieux. Cela ressemble à un travail pour APL.
winest l'interface publique. Il prend les dimensions du tableau, le convertit en une représentation du tableau et le transmet àw .
west l'algorithme minimax. Il prend un état de plateau, essaie tous les coups, construit une liste dont les éléments correspondent aux coups gagnants et retourne True si la liste est vide. Avec la valeur par défaut f=print, la création de la liste a pour effet secondaire d'imprimer les coups gagnants. Le nom de la fonction avait plus de sens lorsqu'il renvoyait une liste de coups gagnants, mais j'ai ensuite déplacé l' notavant de la liste pour économiser de l'espace.
for r,p in enumerate(b)for c in xrange(p) if(r+c): Itérer sur tous les mouvements possibles. 1 1est traité comme un pas légal, simplifiant un peu le cas de base.
b[:r]+[min(i,c)for i in b[r:]]: Construire l'état du plateau après le mouvement représenté par des coordonnées retc .
w(b[:r]+[min(i,c)for i in b[r:]],max): Recurse pour voir si le nouvel état est un état perdant. maxest la fonction la plus courte que j'ai pu trouver qui prendrait deux arguments entiers et ne se plaindrait pas.
f(r+1,c+1): Si fest imprimer, imprime le mouvement. Peu importef soit, il produit une valeur pour remplir la longueur de la liste.
not [...]: notrenvoie Truepour les listes vides et Falsepour les non vides.
Code Python 2 original, complètement non golfé, y compris la mémorisation pour gérer des entrées beaucoup plus importantes:
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))]
Démo:
>>> 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