# 找到最佳的起步动作

14

Chomp是一款两人游戏，具有矩形的碎片设置。每个玩家轮流移除任何棋子，以及其上方和右侧的所有棋子。拿到左下角的人都会输。可以很容易地证明第一个玩家总是有获胜的举动（除非有1 x 1矩形）；找到它。

1. 输入为矩形的尺寸（两个数字）
2. 输出是获胜举动的位置（两个数字）
3. 如果有多个获胜举动，则可以输出其中任何一个。

# 例子

``````XXX--
XXXXX
XXXXX
``````

``````X---
X---
X---
XXXX
``````

# 奖金

kitcar2000 2014年

@Kitcar你是对的；固定。
Ypnypn 2014年

Undergroundmonorail

@undergroundmonorail 1从左下角开始的索引。第一个索引是水平轴，第二个索引是垂直索引。
Martin Ender 2014年

2

7

### GolfScript，82（108 97个字符-15个奖励）

``````~),1/{{:F\$0=),{F\+}/}%}@(*(0*\{1\${1\$\{\(@<},=},{1\$\{\(@>},+(-!},:Y!{.,/+0}*;}/;Y{.-1=.@?)' '@)n}/
``````

``````> 5 3
4 3

> 5 4
3 3

> 6 6
2 2
``````

``````~                   # Evaluate the input (stack is now w h)

# BUILDING THE COMPLETE STATE SPACE
# Iteratively builds the states starting with 1xh board, then 2xh board, ...

),1/                # Generate the array [[0] [1] ... [h]] which is the space for 1xh
{                   # This loop is now ran w-1 times and each run adds all states for the
# board with one additional column
{                 # The {}/] block simply runs for each of the existing states
:F\$0=           #   Take the smallest entry (which has to be the last one)
),              #   For the last column all values 0..x are possible
{F\+}/          #     Append each of these values to the smaller state
}%
}@(*

# The order ensures that the less occupied boards are first in the list.
# Thus each game runs from the end of the list (where [h h ... h] is) to
# the start (where [0 0 ... 0] is located).

# RUN THROUGH THE SEARCH SPACE
# The search algorithm therefore starts with the empty board and works through all
# possible states by simply looping over this list. It builds a list of those states
# which are known as non-winning states, i.e. those states where a player should
# aim to end after the move

(                   # Skips the empty board (which is a winning configuration)
0*\                 # and makes an empty list out of it (which will be the list of
# known non-winning states (initially empty))
{                   # Loop over all possible states
1\$                #   Copy of the list of non-winning states
{                 #   Filter those which are not reachable from the current state,
#   because at least one column has more pieces that the current
#   board has
1\$\{\(@<},=
},
{                 #   Filter those which are not reachable from the current state,
#   because no valid move exists
1\$\{\(@>},+     #     Filter those columns which are different between start and
#     end state
(-!             #     If those columns are all of same height it is possible to move
},
:Y                #   Assign the result (list of all non-winning states which are
#   reachable from the current configuration within one move)
#   to variable Y

!{                #   If Y is non-empty this one is a winning move, otherwise
#   add it to the list
.,/+
0               #     Push dummy value
}*;
}/
;                   # Discard the list (interesting data was saved to variable Y)

# OUTPUT LOOP
# Since the states were ordered the last one was the starting state. The list of
# non-winning states were saved to variable Y each time, thus the winning moves
# from the initial configuration is contained in this variable.

Y{                  # For each item in Y
.-1=.@?)          #   Get the index (1-based) of the first non-h value
' '               #   Append a space
@)                #   Get the non-h value itself (plus one)
n                 #   Append a newline
}/
``````

+1对于解决方案本身以及非常受注释的代码
Xuntar 2014年

user2357112支持Monica 2014年

Mouq

8

## 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)]
``````

• `win`是公共接口。它采用了板子的尺寸，将其转换为板子表示，并将其传递给`w`
• `w`是minimax算法。它处于棋盘状态，尝试所有移动，构建一个列表，该列表的元素与获胜移动相对应，如果列表为空，则返回True。使用默认设置`f=print`，建立列表具有打印获胜举动的副作用。该函数名称在返回获胜动作列表时更有意义，但后来我将`not`了列表的前面以节省空间。
• `for r,p in enumerate(b)for c in xrange(p) if(r+c)`：遍历所有可能的动作。`1 1`被视为不合法的举动，从而简化了基本案例。
• `b[:r]+[min(i,c)for i in b[r:]]`：在坐标`r`和表示的移动之后构造板的状态`c`
• `w(b[:r]+[min(i,c)for i in b[r:]],max)`：递归查看新状态是否为丢失状态。`max`是我能找到的最短的函数，它将接受两个整数参数并且不会抱怨。
• `f(r+1,c+1)`：如果`f`正在打印，则打印移动。不管`f`是什么，它都会产生一个值来填充列表长度。
• `not [...]``not`返回`True`空列表和`False`非空列表。

``````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))]
``````

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

davidsbro 2014年

@davidsbro：是的，这是任何大于1x1的正方形板的制胜法宝，但我的代码尚未计算出来。
user2357112支持Monica 2014年

2

# Perl 6：113108个字符-15 = 93分

``````sub win(*@b){map ->\i,\j{\$(i+1,j+1) if @b[i][j]&&!win @b[^i],@b[i..*].map({[.[^j]]})},(^@b X ^@b[0])[1..*]}
``````

``````loop {
my (\$y, \$x) = get.words;
.say for @(win [1 xx \$x] xx \$y)
}
``````

``````my %cache;
sub win (*@b) {
%cache{
join 2, map {(\$^e[\$_]??1!!0 for ^@b[0]).join}, @b
} //= map ->\i,\j{
\$(i+1,j+1) if @b[i][j] and not win
@b[^i], @b[i..*].map({[.[^(* min j)]]}).grep: +*;
},(^@b X ^@b[0])[1..*]
}
``````