2

私はまだpythonを学んでいます。ボードの状態が次のような場合に、プレーヤーが三目並べのゲームに勝ったかどうかを判断するために、このメソッドを作成しました。'[['o','x','x'],['x','o','-'],['x','o','o']]'

def hasWon(board):
  players = ['x', 'o']
  for player in players:
    for row in board:
      if row.count(player) == 3:
        return player
    top, mid, low = board
    for i in range(3):
      if [ top[i],mid[i],low[i] ].count(player) == 3:
        return player
    if [top[0],mid[1],low[2]].count(player) == 3:
        return player
    if [top[2],mid[1],low[0]].count(player) == 3:
        return player
  return None

3文字のリストを数回チェックし、次のようにチェックを独自のメソッドにリファクタリングできることに気づきました。

def check(list, player):
  if list.count(player) == 3:
    return player

...しかし、実際には次のような行を変更するだけであることに気付きました。

 if [ top[i],mid[i],low[i] ].count(player) == 3:
    return player

に:

  if check( [top[i],mid[i],low[i]], player ):
    return player

...率直に言って、あまり改善されていないようです。これをリファクタリングするより良い方法はありますか? または、一般的に、より Pythonic なオプションですか? ぜひ聞いてみたいです!

4

7 に答える 7

5

私は使うかもしれません

def check(somelist, player):
  return somelist.count(player) == 3

編集: @Andrew がコメント (tx @Andrew!) で提案したように、さらに良いことができます。

def check(somelist, player):
  return somelist.count(player) == len(somelist)

-- をハードコーディングせずに3-- これは、別の優れた代替手段も示唆しています。

def check(somelist, player):
  return all(x==player for x in somelist)

これは、「リスト内のすべての項目が等しいplayer」と非常に直接的に読み取ります。一般的なポイントは、別のメソッドにリファクタリングすることで、そのメソッドの実装を試すことができるということです。もちろん、ここでのコードは非常に単純であるため、利点は同様に控えめですが、移行するときに覚えておくべき優れた点です。より複雑なコード。

お気づきのように、とにかく bool だけが必要なため、これにより、はるかに単純なアプローチが可能になります。bool 式を実行するのではなく、bool 式を返すだけifです。独自の識別子のような組み込みの名前を決して使用しないことが重要listです。これは、言語の「魅力的な迷惑」です...;-)。

つまり、Python はそのビルトインに list、bool、sum などの魅力的な名前をたくさん使用しているため、それらの名前の 1 つを誤って自分の変数に使用していることに気付くのは簡単です。悪いことが起こっているようです...たとえば、タプルをリストに変換し、明らかに最適なソリューションを使用する必要があるときまでx = list(thetuple)...そして、エラーを理解し、解決するために私たちの努力を費やすことになります.listその名前の組み込み型以外の意味で使用されていました。

したがって、それぞれのビルトインを示す以外の目的でこれらの素敵なビルトイン名を使用しないという習慣を身につけてください.

コードに戻ると、解凍しないことで得られる簡潔さを考慮することができます (コードboard非常に読みやすいため、難しい決定です... しかし、少し冗長に見えるかもしれません)。

for i in range(3):
  if check([row[i] for row in board], player):
    return player
if check([row[i] for i, row in enumerate(board)], player):
    return player
if check([row[2-i] for i, row in enumerate(board)], player):
    return player

最後に、私はあなたの選択に固執すると思います-もしあったとしても、より読みやすく、ほんのわずかに冗長です-しかし、代替案を知っておくのはいいことだと思います-ここでは、内包表記をリストしenumerate、リストを生成します3 つの可能性を「手動でコーディング」する代わりにチェックする必要があります。

于 2010-04-28T14:18:04.463 に答える
2

の上にカスタムイテレータを作成するだけboardです。

def get_lines(board):
  nums = range(3)
  for i in nums: 
    yield (board[i][j] for j in nums) #cols
  for j in nums: 
    yield (board[i][j] for i in nums) #rows
  yield (board[i][i] for i in nums) #diag \
  yield (board[i][2-i] for i in nums) #diag /

def get_winner(board): #a bit too indented
  for line in get_lines(board): #more expensive, so go through it only once
    for player in 'x', 'o':
      if line == player, player, player: #other way to check victory condition
        return player
  return None

board明らかに、これらは本当にクラスのメソッドでなければなりません:)

于 2010-04-28T14:48:04.627 に答える
2

checkあまり意味がない代わりに、より良い名前を使用できます。経験則として、コードのピースに適切な名前を思い付くことができれば、たとえそれが 1 行のコードであっても、それを別の関数に移動することが有益である可能性があります。allsameここでの選択肢の1つかもしれません。

def winner(board):
    main_diag = [row[i] for i, row in enumerate(board)]
    aux_diag = [row[len(board) - i - 1] for i, row in enumerate(board)]   
    for triple in board + zip(*board) + [main_diag, aux_diag]: 
        if allsame(triple):         
           return triple[0]

def allsame(lst):    
    return all(x == lst[0] for x in lst)
于 2010-04-28T14:55:49.433 に答える
1

そして今、完全に異なる何かのために:

9つの要素のリストでボードを表します。各要素は、-1(X)、1(O)、または0(空)にすることができます。

WIN_LINES = (
    (0, 1, 2),
    (3, 4, 5),
    (6, 7, 8),
    (0, 3, 6),
    (1, 4, 7),
    (2, 5, 8),
    (2, 4, 6),
    (0, 4, 8),
    )

def test_for_win(board):
    for line in WIN_LINES:
        total = sum(board[point] for point in line)
        if abs(total) == 3:
            return total // 3
    return None

改良:

WIN_LINES = (
    0, 1, 2,
    3, 4, 5,
    6, 7, 8,
    0, 3, 6,
    1, 4, 7,
    2, 5, 8,
    2, 4, 6,
    0, 4, 8,
    )

def test_for_win(board):
    wpos = 0
    for _unused in xrange(8):
        total  = board[WIN_LINES[wpos]]; wpos += 1
        total += board[WIN_LINES[wpos]]; wpos += 1
        total += board[WIN_LINES[wpos]]; wpos += 1
        if total ==  3: return  1
        if total == -3: return -1
    return None
于 2010-04-28T22:45:47.463 に答える
1

個人的には、読みやすくするための最善の策は、関数をバブルアウトして、ボードのrows()、columns()、およびdiags()をリストのリストとして提供することだと思います。次に、これらを反復して均一にチェックできます。さらに、rows()、columns()、および diags() の出力を追加する allTriples() を定義して、単一の簡潔なループでチェックを行うことができます。これらの関数がオブジェクト メソッドになるように、おそらくボードもオブジェクトにします。

于 2010-04-28T14:37:38.210 に答える
0

ただのアイデア

def hasWon(board):
  players = ['x', 'o']
  for player in players:
    top, mid, low = board
    game = board + [[ top[i],mid[i],low[i]] for i in range(3)] + [top[0],mid[1],low[2]] +[top[2],mid[1],low[0]]
    if 3 in [l.count(player) for l in game] :
      return player
  return None
于 2010-04-28T14:30:28.470 に答える
0

あなたのソリューションは問題ありません-正しく、読みやすく、理解できます。

それでも、速度を最適化したい場合は、文字列ではなく数字の 1 次元配列を使用し、各数値をできるだけ少ない回数で検索しようとします。すべてのフィールドを 1 回だけチェックする、非常に厄介な解決策が必ずあります。私は今これを構築したくありません。:) 可能性のある動きの検索ツリー全体を探索しながら、対戦する AI を実装したい場合、そのようなことは重要かもしれません. そこでは、効率的な勝敗チェックが必要になります。

于 2010-04-28T14:41:02.140 に答える