Python -从位置对象的2D数组打印ASCII笛卡尔坐标网格

q7solyqu  于 2022-12-30  发布在  Python
关注(0)|答案(2)|浏览(117)

非常抱歉,但是这将是一个很长的问题,所以作为我之前关于STDIN的问题的一个分支,不幸的是,似乎没有足够多的问题与我的特定问题相似,至少在Python中没有(也不需要使用numpy之类的东西,这比我的水平高了一点)。我确实有很多已经实现的代码,所以希望这会有所帮助(虽然它不是最干净的,正如我上面提到的,它相当长)。
目前,我的问题是打印出表示类对象的2D数组的ASCII艺术,以便ASCII在简单的x,y笛卡尔坐标网格的方向上准确地描述它们。
我有几个相互交互的类,第一个类构造包含两个整数的position对象,表示笛卡尔坐标对的x和y:

class Pos:
    def __init__(self, x, y):
        self.x = x
        self.y = y

第二个类根据用户在程序的另一部分中提供的一些大小参数构造了一个“正方形”网格,在这里实现为一个填充了这些位置对象的2D数组:

class Grid:
    def __init__(self, size):
        assert size > 0
        self.size = size
        self.grid = []
        #1  Perhaps there is something wrong with my 2D implementation...
        for x in range(size):
            sub_grid = []
            for y in range(size):
                sub_grid.append(Pos(x, y))
            self.grid.append(sub_grid)

这个类中还有一个方法,它打印出网格的ASCII艺术图,作为用户的外部表示(这是比较麻烦的地方):

def print(self):
    #  This is the main ASCII-art.
    size_check = self.size - 1
    if self.size > 10:
        print('   +' + ('-' * (((self.size * 2) + 1)) + '+'))
    else:
        print('  +' + ('-' * (((self.size * 2) + 1)) + '+'))
    counter = 0
    #2  ...or perhaps it's something wrong with this iterative condition.
    for x in self.grid[::-1]:
        if self.size > 10:
            if size_check > 9:
                print(self.size - 1 - counter, '|', end=' ')
            else:
                print('', self.size - 1 - counter, '|', end=' ')
        else:
            print(self.size - 1 - counter, '|', end=' ')
        counter += 1
        size_check -= 1
        for y in x:
            #  This is only here to check if the coords are in the correct position in the art.
            ^^if y.x == 5 and y.y == 8:
                print('O ', end='')^^
            else:
                print('. ', end='')
        print('|')
    if self.size > 10:
        print('   +' + ('-' * (((self.size * 2) + 1)) + '+'))
        print('     ', end='')
    else:
        print('  +' + ('-' * (((self.size * 2) + 1)) + '+'))
        print('    ', end='')

    #  This is simply support for grid sizes greater than 10.
    if self.size > 10:
        for x in self.grid:
            if x[0].x <= 9:
                print('', '', end=' ')
            elif x[0].x > 9 and x[0].x < (self.size - 1):
                strng = str(x[0].x)
                print(int(strng[0]), end=' ')
            elif x[0].x == (self.size - 1):
                strng = str(x[0].x)
                print(int(strng[0]))
        print('     ', end='')
        for x in self.grid:
            if x[0].x <= 9:
                print(x[0].x, '', end='')
            elif x[0].x > 9 and x[0].x < (self.size - 1):
                strng = str(x[0].x)
                print(int(strng[1]), end=' ')
            elif x[0].x == (self.size - 1):
                strng = str(x[0].x)
                print(int(strng[1]), end=' ')
    else:
        for x in self.grid:
            if x[0].x < (self.size - 1):
                print(x[0].x, '', end='')
            elif x[0].x == (self.size - 1):
                print(x[0].x, end='')
    print()

我知道这是很多,但它成功打印出来的看起来像这样(给定大小= 10):

+---------------------+
9 | . . . . . . . . . . |
8 | . . . . . . . . . . |
7 | . . . . . . . . . . |
6 | . . . . . . . . . . |
5 | . . . . . . . . . . |
4 | . . . . . . . . . . |
3 | . . . . . . . . . . |
2 | . . . . . . . . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
  +---------------------+
    0 1 2 3 4 5 6 7 8 9

现在看起来还不错,直到我试着显示位置对象应该位于何处,在上面的代码中,我用^^标记了一个条件语句,它在坐标(5,8)处打印出一个“O”,结果如下:

+---------------------+
9 | . . . . . . . . . . |
8 | . . . . . . . . . . |
7 | . . . . . . . . . . |
6 | . . . . . . . . . . |
5 | . . . . . . . . O . |
4 | . . . . . . . . . . |
3 | . . . . . . . . . . |
2 | . . . . . . . . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
  +---------------------+
    0 1 2 3 4 5 6 7 8 9

理想的输出实际上应该如下所示:

+---------------------+
9 | . . . . . . . . . . |
8 | . . . . . O . . . . |
7 | . . . . . . . . . . |
6 | . . . . . . . . . . |
5 | . . . . . . . . . . |
4 | . . . . . . . . . . |
3 | . . . . . . . . . . |
2 | . . . . . . . . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
  +---------------------+
    0 1 2 3 4 5 6 7 8 9

正如您所看到的,ASCII艺术品的打印方式在外部看起来就像x轴和y轴交换了一样,即使在内部任何位置对象的坐标都正确响应。我在上面的代码中写了两个注解,我推测问题可能是什么。我最好的猜测要么是我如何实现网格本身,要么是我如何打印艺术品;但我需要你帮我弄清楚。
任何和所有的帮助,一如既往,完全感谢;我再次道歉!

92dk7w1h

92dk7w1h1#

我建议使用字符串操作而不是直接打印来构建行。这将使代码更简单,并允许用模板字符串中的模式替换典型的行内容。
例如:

# prepare the empty content
rows = 10
cols = 10
content = [["."]*cols for _ in range(rows)]

# assign values at coordinates as needed (based on your grid)
content[5][8]  = "O"
grid = [(4,1,"H"),(6,3,"L"),(5,2,"E"),(4,6,"R"),(7,4,"L"),(6,6,"W"),(3,6,"L"),(2,6,"D"),(5,6,"O")]
for (y,x,c) in grid: content[y][x] = c

# build frame
width       = len(str(max(rows,cols)-1))
contentLine = "# | values |"

dashes      = "-".join("-"*width for _ in range(cols))
frameLine   = contentLine.replace("values",dashes)
frameLine   = frameLine.replace("#"," "*width)
frameLine   = frameLine.replace("| ","+-").replace(" |","-+")

# print grid
print(frameLine)
for i,row in enumerate(reversed(content),1):
    values = " ".join(f"{v:{width}s}" for v in row)
    line   = contentLine.replace("values",values)
    line   = line.replace("#",f"{rows-i:{width}d}")
    print(line)
print(frameLine)

# x-axis numbers
numLine = contentLine.replace("|"," ")
numLine = numLine.replace("#"," "*width)
colNums = " ".join(f"{i:<{width}d}" for i in range(cols))
numLine = numLine.replace("values",colNums)
print(numLine)

输出:

+---------------------+
9 | . . . . . . . . . . |
8 | . . . . . O . . . . |
7 | . . . . L . . . . . |
6 | . . . L . . W . . . |
5 | . . E . . . O . . . |
4 | . H . . . . R . . . |
3 | . . . . . . L . . . |
2 | . . . . . . D . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
  +---------------------+
    0 1 2 3 4 5 6 7 8 9

行= 12且列= 15时的输出:

+----------------------------------------------+
11 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
10 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
 9 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
 8 | .  .  .  .  .  O  .  .  .  .  .  .  .  .  .  |
 7 | .  .  .  .  L  .  .  .  .  .  .  .  .  .  .  |
 6 | .  .  .  L  .  .  W  .  .  .  .  .  .  .  .  |
 5 | .  .  E  .  .  .  O  .  .  .  .  .  .  .  .  |
 4 | .  H  .  .  .  .  R  .  .  .  .  .  .  .  .  |
 3 | .  .  .  .  .  .  L  .  .  .  .  .  .  .  .  |
 2 | .  .  .  .  .  .  D  .  .  .  .  .  .  .  .  |
 1 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
 0 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
   +----------------------------------------------+
     0  1  2  3  4  5  6  7  8  9  10 11 12 13 14
iyr7buue

iyr7buue2#

下面是一个使用rust的实现,它分为几个步骤

      1. Grid结构**
pub struct Grid {
    cells: Vec<Vec<char>>,
    num_rows: usize,
    num_cols: usize,
}

Grid结构表示一个字符网格,它有三个字段:

  • cells:表示网格中单元格的字符的2D向量。
  • num_rowsusize,表示网格中的行数。
  • num_colsusize表示网格中的列数。
      1. Alignment枚举**
#[derive(Debug, PartialEq)]
enum Alignment {
    Left,
    Center,
    Right,
}

Alignment枚举表示打印网格时单元格值的可能对齐方式。它有三种变体:

  • Left:单元格值左对齐。
  • Center:将单元格值居中对齐。
  • Right:单元格值右对齐。
      1. new方法**
fn new(num_rows: usize, num_cols: usize, empty_cell_char: char) -> Self {
        let cells = vec![vec![empty_cell_char; num_cols]; num_rows];
        Self {
            cells,
            num_rows,
            num_cols,
        }
    }
}

new方法是Grid结构的构造函数方法,它有三个参数:

  • num_rows:一个usize,表示网格中的行数。
  • num_cols:一个usize,表示网格中的列数。
  • empty_cell_charchar,表示填充空单元格的字符。

它创建一个具有给定行数和列数的新Grid对象,并用给定字符填充单元格。

      1. set_cell方法**
fn set_cell(&mut self, row: usize, col: usize, character: char) {
        self.cells[row][col] = character;
    }
}

set_cell方法在网格的特定单元格中设置字符。它有三个参数:

  • rowusize,表示单元格的行数。
  • colusize,表示单元格的列号。
  • characterchar,表示要在单元格中设置的字符。

它设置给定行和列的单元格中的字符。

      1. draw方法**
impl Grid {
    fn draw(
        &self,
        cell_width: usize,
        border_char: char,
        row_separator_char: char,
        col_separator_char: char,
        x_axis_separator_char: char,
        alignment: Alignment,
    ) {
        let frame_top_bottom = (0..self.num_cols)
            .map(|_| border_char.to_string().repeat(cell_width))
            .collect::<Vec<_>>()
            .join(&border_char.to_string());

        let row_template = format!(
            "{row_num}{row_separator}{row_values}{row_values_separator}",
            row_num = "{row_num}",
            row_separator = row_separator_char,
            row_values = "{row_values}",
            row_values_separator = row_separator_char
        );

        let row_num = row_template.replace(
            "{row_num}",
            format!("{:width$}", "", width = cell_width).as_str(),
        );

        let frame_top = row_num.replace("{row_values}", &frame_top_bottom);

        println!("{}", frame_top);

        for (row_num, row) in self.cells.iter().rev().enumerate() {
            let row_num_str = format!("{:width$}", self.num_rows - row_num - 1, width = cell_width);
            let row_values: Vec<String> = row
                .iter()
                .map(|cell| {
                    let aligned_string: String = match alignment {
                        Alignment::Left => format!("{:<width$}", cell, width = cell_width),
                        Alignment::Center => format!("{:^width$}", cell, width = cell_width),
                        Alignment::Right => format!("{:>width$}", cell, width = cell_width),
                    };

                    aligned_string
                })
                .collect();

            println!(
                "{}",
                row_template
                    .replace(
                        "{row_num}",
                        format!("{:<width$}", row_num_str, width = cell_width).as_str()
                    )
                    .replace(
                        "{row_values}",
                        &row_values.join(&col_separator_char.to_string())
                    )
            );
        }
        println!("{}", frame_top);

        let col_nums: Vec<String> = (0..self.num_cols)
            .map(|col| {
                let aligned_string: String = match alignment {
                    Alignment::Left => format!("{:<width$}", col, width = cell_width),
                    Alignment::Center => format!("{:^width$}", col, width = cell_width),
                    Alignment::Right => format!("{:>width$}", col, width = cell_width),
                };

                aligned_string
            })
            .collect();
        let x_axis = row_num.replace(
            "{row_values}",
            &col_nums.join(&x_axis_separator_char.to_string()),
        );
        println!("{}", x_axis);
    }
}

draw方法使用给定的参数将网格打印到控制台。

  • cell_widthusize,表示打印网格时每个单元格的宽度。
  • border_charchar,表示用于网格边框的字符。
  • row_separator_charchar,表示用作网格各行之间分隔符的字符。
  • col_separator_char:一个char,表示用作网格各列之间分隔符的字符。
  • x_axis_separator_charchar,表示用作网格底部列号之间分隔符的字符。
  • alignmentAlignment值,表示打印网格时单元格值的对齐方式。

draw方法首先使用border_charcell_width生成网格的上边框和下边框,然后为网格的每一行创建一个模板,该模板由行号、行分隔符、该行的单元格值和行值分隔符组成。
然后,它从下到上迭代网格的行,并使用模板、当前行号和单元格值为每一行生成一个字符串,然后将每一行打印到控制台。
最后,它使用alignmentx_axis_separator_char在网格底部生成列号,并将它们打印到控制台。

      1. main函数**

复制代码

fn main() {
    let mut grid = Grid::new(3, 3, ' ');
    grid.set_cell(0, 0, 'X');
    grid.set_cell(1, 1, 'O');
    grid.set_cell(2, 2, 'X');
    grid.draw(
        4,
        '+',
        '|',
        ' ',
        ' ',
        Alignment::Right,
    );
}

main函数新建一个3行3列的Grid对象,并使用set_cell方法设置单元格中的字符,然后调用Grid对象上的draw方法将其打印到控制台,单元格宽度为4,边框字符为'+'。行分隔符字符'|'、列分隔符字符' '、x轴分隔符字符' '以及对齐方式Alignment::Right

下面的表中是它的摘要

| 步骤|说明|
| - ------| - ------|
| 定义Grid结构|Grid结构表示具有三个字段的字符网格:cells,表示网格中的单元的字符的2D向量; num_rowsusize表示网格中的行数;以及num_colsusize表示网格中的列数。|
| 定义Alignment枚举|Alignment枚举表示打印网格时单元格值的可能对齐方式。它有三种变体:Left,将单元格值左对齐; Center,将单元格值与中心对齐; Right,它将单元格值右对齐。|
| 定义new方法|new方法是Grid结构的构造函数。它有三个参数:num_rowsusize表示网格中的行数; num_colsusize表示网格中的列数; empty_cell_char,一个char,表示要填充空单元格的字符。它创建一个具有给定行数和列数的新Grid对象,并用给定字符填充单元格。|

| 定义set_cell方法|set_cell方法在网格的特定单元格中设置字符。它有三个参数:rowusize表示单元格的行数; colusize表示单元格的列号; character,一个char,表示要在单元格中设置的字符,它设置给定行和列的单元格中的字符。|
| 定义draw方法|draw方法使用给定的参数将网格打印到控制台。cell_widthusize表示当打印网格时每个单元的宽度; border_char,表示用于网格边界的字符的char; row_separator_charchar表示用作网格的行之间的分隔符的字符; col_separator_charchar表示用作网格的列之间的分隔符的字符; x_axis_separator_charchar表示用作网格底部列号之间分隔符的字符;和alignmentAlignment值表示打印网格时单元格值的对齐方式。它首先使用border_charcell_width生成网格的上边框和下边框。然后为网格的每一行创建一个模板,该模板由行号、行分隔符、该行的单元格值和行值分隔符。然后,它从下到上迭代网格的行,并使用模板、当前行号和单元格值为每一行生成一个字符串。然后,它将每一行打印到控制台。最后,它使用alignmentx_axis_separator_char在网格底部生成列号。并打印到控制台。|
| 定义main函数|main函数新建一个3行3列的Grid对象,并使用set_cell方法设置单元格中的字符,然后调用Grid对象上的draw方法将其打印到控制台,单元格宽度为4,边框字符为'+'。行分隔符""|

以下是该表的概要

| 步骤|说明|
| - ------| - ------|
| 定义Grid结构|Grid结构表示具有三个字段的字符网格:cells,表示网格中的单元的字符的2D向量; num_rowsusize表示网格中的行数;以及num_colsusize表示网格中的列数。|
| 定义Alignment枚举|Alignment枚举表示打印网格时单元格值的对齐方式。它有三种变体:一个一米124一个x、一个一米125一个x和一个一米126一个x。|
| 定义new方法|new方法创建具有给定行数和列数的新Grid对象,并用给定字符填充单元格。|
| 定义set_cell方法|set_cell方法设置网格的特定单元格中的字符。|
| 定义draw方法|draw方法使用给定的参数将网格打印到控制台。|
| 定义main函数|main函数创建一个Grid对象并设置其单元格中的字符,然后使用draw方法将网格打印到控制台。|

相关问题