6

Pythonで構築しているMUD(または可能であればそれ以上)で、プレイヤーの現在の部屋から半径2マップを描画する必要があります。self.exits = {'west':1, 'north':2}ルームは、キーが値 (隣接するルームの UID) が配置されている方向であるコンテナとして設定されます。部屋はこの方法でのみリンクされます。self.location が 0 のプレイヤーが「n」と入力すると、上記の変数に基づいてロケーションが 2 になり、そのルームのコンテンツにはプレイヤーの UID が追加されます。

そこで、上記の変数に基づいて、次のようなマップを表示したいと思います。ここで、「u」はプレーヤーの現在の場所です。

    [ ]
     |
[ ]-[u]

これは半径 1 にすぎないため、この部分を達成しました。ここに、これを行った方法の小さな (ここに投稿するために大幅に変更された) スニペットを示します。

mloc = '[u]'
mn = '   '
mw = '   '
spn= ' '
spw= ' '
for Exit in room.exits.keys():
  if Exit == 'north':
    mn = '[ ]'
    spn = '|'
  if Exit == 'west': 
    mw = '[ ]-'
# player.hear() is our function for printing a line to the player's screen
player.hear('    '+mn)
player.hear('     '+sp)
player.hear(mw+mloc)

私の狂気の中で、私はこれを8つの異なる方向すべて(斜め、上または下を含まない)で機能させることができました。しかし、最初の for ループで解析したばかりの部屋を for ループし、それらを描画し、すべてのスペースを空け、'\' や ' |' 交わる道があれば。この小さなタスクはすぐに悪夢のようになり、完了する前に 200 行にもなりました。

もう 1 つのハードルは、1 行ずつしか印刷できないことです。したがって、マップの高さが 50 文字の場合、player.hear()50 行にする必要がありますが、これは反対ではありません。回答を投稿する前に、そのことを覚えておいてください。

また、フォーマットについてもうるさいわけではありません。世界中を旅しているプレイヤーを支援するために、「一目でわかるマップ」が欲しいだけです。

みんなありがとう。十分な情報を提供したことを願っています。そうでない場合はお知らせください。(これは、私が参照している(未完成で恐ろしい)モジュール全体へのリンクです。Map.py

4

3 に答える 3

13

このコードは深刻な問題を抱えています。ゼロから設計を始めましょう。これは、クラスとデータ構造を設計および構築する方法の良い教訓になることを願っています。

まず、Mapクラスを中心にコードを編成する必要があります。これにより、部屋がグリッドとして表されます。「部屋 1」、「部屋 2」など (地図上で追跡するのが非常に難しい) について考えるのではなく、部屋を座標の観点から考える必要があります。

現在、プレイヤーが行ったことのある部屋しか見ていない、プレイヤーがマップの中央にとどまっている、斜めのパスなど、最初は無視している可能性のある機能がいくつかあります。それらが必要な場合は、基本的な機能が動作するようになったら、後でそれらを配置できます。今のところ、次のようなものを目指しています。

[ ]-[u] [ ] [ ]
 |
[ ]-[ ]-[ ] [ ]
     |
[ ]-[ ]-[ ] [ ]
 |
[ ]-[ ]-[ ]-[ ]

つまり、一部の部屋が接続され、他の部屋が接続されていないグリッドとして表しています。次のように、各部屋に座標ペアを持たせましょう。

      0   1   2   3
   0 [ ]-[u] [ ] [ ]
      |
   1 [ ]-[ ]-[ ] [ ]
          |
   2 [ ]-[ ]-[ ] [ ]
      |
   3 [ ]-[ ]-[ ]-[ ]

x を上に、y を横にします。左上が (0, 0)、その[u]中にあるのが (0, 1) です。

さて、私たちのMapクラスのコンポーネントは何でしょうか?

  1. マップの高さ: 整数

  2. マップの幅: 整数)

  3. player_x, player_y: プレイヤーの座標

  4. 可能なパス: 移動できる部屋のペアのリスト。上記のマップは次のように表されます。

    [((0, 0), (1, 0)), ((0, 0), (1, 0)), ((1, 0), (1, 1)), ((1, 1), (2, 1)),
     ((1, 0), (1, 2)), ((0, 2), (1, 2)), ((1, 2), (2, 2)), ((0, 2), (0, 3)),
     ((0, 3), (1, 3)), ((1, 3), (2, 3)), ((2, 3), (3, 3))]
    

大きなタプルが最初になるように各ペアを並べたことに注意してください (これは後で重要です)。

デザインができたので、そのMapクラスを書きましょう。

必要なメソッドは、 、 、および初期化子の 4 つprint_mapですmove。初期化は簡単です。上記の 4 つの属性を設定するだけです。

class Map:
    def __init__(self, height, width, player_x, player_y, paths):
        self.height = height
        self.width = width
        self.x = player_x
        self.y = player_y
        self.paths = paths

今、move非常に簡単です。方向 n/e/s/w を指定すると、次のようになります。

    def move(self, direction):
        if direction == "n":
            if ((self.x, self.y - 1), (self.x, self.y)) not in self.paths:
                print "Cannot go north"
            else:
                self.y -= 1

「北」のmove関数は、私たちがいる部屋の上に部屋への道があるかどうかをチェックするだけです。

ここで最も興味深いのは、マップの印刷です。self.heightこれを行うには、行 (0 から) と列 (0 から)をループしますself.width。(print:文字列の後に改行またはスペースが自動的に挿入されるため、この状況では使用できません。代わりにsys.stdout.writeを使用します。

def print_map(self):
    for y in range(0, self.height):
        # print the yth row of rooms
        for x in range(0, self.width):
            if self.x == x and self.y == y:
                sys.stdout.write("[u]")  # this is the player's room
            else:
                sys.stdout.write("[ ]")  # empty room
            # now see whether there's a path to the next room
            if ((x, y), (x + 1, y)) in self.paths:
                sys.stdout.write("-")
            else:
                sys.stdout.write(" ")
        # now that we've written the rooms, draw paths to next row
        print  # newline
        for x in range(0, self.width):
            sys.stdout.write(" ")  # spaces for above room
            if ((x, y), (x, y + 1)) in self.paths:
                sys.stdout.write("|  ")
            else:
                sys.stdout.write("   ")
        print

それでは、すべてをまとめて試してみましょう。コードは次のとおりです。

import sys

class Map:
    def __init__(self, height, width, player_x, player_y, paths):
        self.height = height
        self.width = width
        self.x = player_x
        self.y = player_y
        self.paths = paths

    def move(self, direction):
        if direction == "n":
            if ((self.x, self.y - 1), (self.x, self.y)) not in self.paths:
                print "Cannot go north"
            else:
                self.y -= 1
        if direction == "s":
            if ((self.x, self.y), (self.x, self.y + 1)) not in self.paths:
                print "Cannot go south"
            else:
                self.y += 1
        if direction == "e":
            if ((self.x, self.y), (self.x + 1, self.y)) not in self.paths:
                print "Cannot go east"
            else:
                self.x += 1
        if direction == "w":
            if ((self.x - 1, self.y), (self.x, self.y)) not in self.paths:
                print "Cannot go west"
            else:
                self.x -= 1

    def print_map(self):
        for y in range(0, self.height):
            # print the yth row of rooms
            for x in range(0, self.width):
                if self.x == x and self.y == y:
                    sys.stdout.write("[u]")  # this is the player's room
                else:
                    sys.stdout.write("[ ]")  # empty room
                # now see whether there's a path to the next room
                if ((x, y), (x + 1, y)) in self.paths:
                    sys.stdout.write("-")
                else:
                    sys.stdout.write(" ")
            # now that we've written the rooms, draw paths to next row
            print  # newline
            for x in range(0, self.width):
                sys.stdout.write(" ")  # spaces for above room
                if ((x, y), (x, y + 1)) in self.paths:
                    sys.stdout.write("|  ")
                else:
                    sys.stdout.write("   ")
            print


paths = [((0, 0), (1, 0)), ((0, 0), (1, 0)), ((1, 0), (1, 1)), ((1, 1),
         (2, 1)), ((1, 1), (1, 2)), ((0, 2), (1, 2)), ((1, 2), (2, 2)),
         ((0, 2), (0, 3)), ((0, 3), (1, 3)), ((1, 3), (2, 3)), ((2, 3),
         (3, 3))]
m = Map(4, 4, 0, 0, paths)

while True:
    m.print_map()
    direction = raw_input("What direction do you want to move? [n/e/s/w] ")
    m.move(direction)

マップを作成し、プレーヤーがその中を移動できるようにするセクションを下部に追加したことに注意してください。実行時の様子は次のとおりです。

Davids-MacBook-Air:test dgrtwo$ python Map.py 
[u]-[ ] [ ] [ ] 
     |          
[ ] [ ]-[ ] [ ] 
     |          
[ ]-[ ]-[ ] [ ] 
 |              
[ ]-[ ]-[ ]-[ ] 

What direction do you want to move? [n/e/s/w] e
[ ]-[u] [ ] [ ] 
     |          
[ ] [ ]-[ ] [ ] 
     |          
[ ]-[ ]-[ ] [ ] 
 |              
[ ]-[ ]-[ ]-[ ] 

What direction do you want to move? [n/e/s/w] s
[ ]-[ ] [ ] [ ] 
     |          
[ ] [u]-[ ] [ ] 
     |          
[ ]-[ ]-[ ] [ ] 
 |              
[ ]-[ ]-[ ]-[ ] 

What direction do you want to move? [n/e/s/w] w
Cannot go west
[ ]-[ ] [ ] [ ] 
     |          
[ ] [u]-[ ] [ ] 
     |          
[ ]-[ ]-[ ] [ ] 
 |              
[ ]-[ ]-[ ]-[ ] 

What direction do you want to move? [n/e/s/w] e
[ ]-[ ] [ ] [ ] 
     |          
[ ] [ ]-[u] [ ] 
     |          
[ ]-[ ]-[ ] [ ] 
 |              
[ ]-[ ]-[ ]-[ ] 

このコードには多くの改善点がありますが (特に、moveメソッドが反復的です)、これは良い出発点です。地図を 20x20 にしてみると、問題なく拡大されることがわかります。

print_mapETA: 次のように、はるかに短い形式で書き直すことができることに注意してください。

def print_map(self):
    for y in range(0, self.height):
        print "".join(["[%s]%s" %
                    ("u" if self.x == x and self.y == y else " ",
                     "-" if ((x, y), (x + 1, y)) in self.paths else " ")
                        for x in range(0, self.width)])
        print " " + "   ".join(["|" if ((x, y), (x, y + 1)) in self.paths
                              else " " for x in range(0, self.width)])

しかし、これはもう少し激しいです。

于 2012-07-28T20:20:54.840 に答える
4

これは、部屋とグリッドが「自分自身を印刷」する演習として行いました。最終的にはより大きなグリッドで実装する方が簡単かもしれないので、これを議論に追加します。

アスキーマップ

+++++++++++++++
+++++++++++++++
+++++++++++++++
++++++   ++++++
++++++ 2 ++++++
++++++/| ++++++
+++  / | ++++++
+++ 3--1 ++++++
+++     \++++++
+++++++++\  +++
+++++++++ 4 +++
+++++++++   +++
+++++++++++++++
+++++++++++++++
+++++++++++++++

このグリッドの各セルは、3 x 3 の '+' 記号のセットです。1 から 4 までの id 値を持つ 4 つのルームが実装されています。ルーム間の接続は、スラッシュ、バックスラッシュ、およびパイプとして表されます。

コード

class Room(object):
    def __init__(self, id, loc, exits):
        self.id = id # unique identifier, may be a name
        self.row = loc[0] # loc is tuple of (row, col)
        self.col = loc[1] 
        # exits is a list where 'X' means no exit and 
        # any other value is id of destination
        self.exits = exits 

    def __str__(self):
        directions = '\\|/- -/|\\'
        room = [ e if e == 'X' else ' ' for e in self.exits ]
        for idx in range(len(room)):
            if room[idx] == ' ':
                room[idx] = directions[idx]
            if room[idx] == 'X':
                room[idx] = ' '
        room[4] = self.id[0] # only print first char of id
        return ''.join(room)

class Map(object):
    def __init__(self, rows, cols, rooms):
        self.rows = rows
        self.cols = cols
        self.rooms = rooms

    def __str__(self):
        world = []
        for i in range(self.rows * 3):
            world.append( ['+++'] * self.cols )
        for room in self.rooms:
            ascii = str(room)
            x = room.col
            y = room.row
            for idx in range(0, 3):
                cell = ascii[idx*3:idx*3+3]
                world[y*3+idx][x] = cell
        return '\n'.join( [ ''.join(row) for row in world ] )


if __name__ == '__main__':
    # set up four rooms
    # each room has unique id (string of any length) and coordinates
    # it also has a set of 8 possible exits, represented as a list where
    # 'X' means exit is blocked and valid exits contain the id of the target room
    r1 = Room(id='1', loc=(2,2), exits=['X','2','X',
                                        '3',' ','X',
                                        'X','X','4',])
    r2 = Room(id='2', loc=(1,2), exits=['X','X','X',
                                        'X',' ','X',
                                        '3','1','X',])
    r3 = Room(id='3', loc=(2,1), exits=['X','X','2',
                                        'X',' ','1',
                                        'X','X','X',])
    r4 = Room(id='4', loc=(3,3), exits=['1','X','X',
                                        'X',' ','X',
                                        'X','X','X',])
    # initialize Map with a list of these four rooms
    map = Map(rows = 5, cols=5, rooms=[r1, r2, r3, r4])
    print map

移動ルーチンは実装されておらず、この表現が機能するためには、単一のキャラクター ID のみが適切に表示されます。

このシステムの利点:

  • 部屋の追加と削除が簡単
  • 部屋の定義は人間が読める
  • 出力関数が過負荷__str__になるため、部屋とグリッドが「自分自身を印刷」します。これは、将来のデバッグや、HTML テーブルのセルなどの将来のフォーマットへの適応に役立つ可能性があります。
于 2012-07-29T05:30:26.610 に答える
0

座標ベースのマップには多くの利点がありますが、多くの高品質の泥が従来の部屋ベースの世界を使用しており、人々が多くの泥と泥のクライアント用にオートマッパーを作成していることを考えると、座標のない泥の自動マップを作成することは問題外ではありません。 . ケースバイケースで競合に対処する必要があります。

ただし、@david-robinson による回答は引き続き使用できます。あなたがしたいことは、ミニマップをプレーヤーをほぼ中心に保ち、出口データを使用して動的に更新することです。地域全体の地図を保存しようとしないでください。動的に更新することで、地理的な競合を回避できます。

マップを泥クライアントに書き込むには、適切な間隔でマップ行を書き、新しい行で終了するだけです。すべてのマップ行をリストに入れて、行の単一のグループとして送信されるようにします (たとえば、マップがソケットから送信されるときに、マップの行の間に他の行を挿入する必要はありません)。泥クライアントはそれを印刷します。適切に(もちろん等幅フォントで)。

于 2012-07-29T06:15:34.463 に答える