Terribly sorry, all, but this is going to be a long one. So as an off-shoot of my previous inquiry regarding STDIN, unfortunately it doesn't appear there are many similar enough questions to my specific issue, at least not in Python (nor without using something like numpy which is a touch above my level). I do have plenty of code which I've already implemented, so hopefully that will help (although it's not the cleanest and as I mentioned above it's rather long).
My issue, as it stands, is with printing out ASCII-art representing a 2D array of class objects such that the ASCII accurately depicts them in the orientation of a simple x,y Cartesian coordinate grid.
I have several classes interacting with each other, the first of which constructs position objects containing two integers, representing the x and y of a Cartesian coordinate pair:
class Pos:
def __init__(self, x, y):
self.x = x
self.y = y
The second class constructs a 'square' grid from some size parameter provided by the user in a different part of my program, implemented here as a 2D array filled with these position objects:
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)
Also within this class is a method which prints out ASCII-art of the grid to act as an external representation for the user (this is the messy bit):
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()
I know this is a lot, but what it successfully prints out looks like so (given size = 10):
+---------------------+
9 | . . . . . . . . . . |
8 | . . . . . . . . . . |
7 | . . . . . . . . . . |
6 | . . . . . . . . . . |
5 | . . . . . . . . . . |
4 | . . . . . . . . . . |
3 | . . . . . . . . . . |
2 | . . . . . . . . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
+---------------------+
0 1 2 3 4 5 6 7 8 9
Now this looks just fine, until I try to show where the position objects are supposed to be located. In the above code I marked with ^^ a conditional statement which prints out an 'O' at the coordinates (5, 8). The result looks like so:
+---------------------+
9 | . . . . . . . . . . |
8 | . . . . . . . . . . |
7 | . . . . . . . . . . |
6 | . . . . . . . . . . |
5 | . . . . . . . . O . |
4 | . . . . . . . . . . |
3 | . . . . . . . . . . |
2 | . . . . . . . . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
+---------------------+
0 1 2 3 4 5 6 7 8 9
The ideal output should actually look like this:
+---------------------+
9 | . . . . . . . . . . |
8 | . . . . . O . . . . |
7 | . . . . . . . . . . |
6 | . . . . . . . . . . |
5 | . . . . . . . . . . |
4 | . . . . . . . . . . |
3 | . . . . . . . . . . |
2 | . . . . . . . . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
+---------------------+
0 1 2 3 4 5 6 7 8 9
As you can see, the ASCII-art is printing in such a way that externally it looks as if the x and y axis have swapped, even though internally the coords at any position object respond correctly. I wrote two comments in the above code where I speculate what the issue might be. My best guesses being either how I implemented the grid itself, or how I'm printing the art; but that's what I need help figuring out.
Any and all help is, as always, wholly appreciated; and I apologize once again!
I would suggest building your lines using string manipulation instead of printing directly. This will make the code simpler and allow for substitution of patterns within a template string for the typical line content.
For example:
# 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)
output:
+---------------------+
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
output with rows=12 and cols=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
1. The Grid
struct
pub struct Grid {
cells: Vec<Vec<char>>,
num_rows: usize,
num_cols: usize,
}
The Grid
struct represents a grid of characters. It has three fields:
cells
: a 2D vector of characters representing the cells in the grid. num_rows
: a usize
representing the number of rows in the grid. num_cols
: a usize
representing the number of columns in the grid. 2. The Alignment
enum
#[derive(Debug, PartialEq)]
enum Alignment {
Left,
Center,
Right,
}
The Alignment
enum represents the possible alignments for the cell values when printing the grid. It has three variants:
Left
: aligns the cell value to the left. Center
: aligns the cell value to the center. Right
: aligns the cell value to the right. 3. The new
method
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,
}
}
}
The new
method is a constructor method for the Grid
struct. It takes three parameters:
num_rows
: a usize
representing the number of rows in the grid. num_cols
: a usize
representing the number of columns in the grid. empty_cell_char
: a char
representing the character to fill empty cells. It creates a new Grid
object with the given number of rows and columns, and fills the cells with the given character.
4. The set_cell
method
fn set_cell(&mut self, row: usize, col: usize, character: char) {
self.cells[row][col] = character;
}
}
The set_cell
method sets the character in a specific cell of the grid. It takes three parameters:
row
: a usize
representing the row number of the cell. col
: a usize
representing the column number of the cell. character
: a char
representing the character to set in the cell. It sets the character in the cell at the given row and column.
5. The draw
method
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);
}
}
The draw
method prints the grid to the console, with the given parameters. It takes six parameters:
cell_width
: a usize
representing the width of each cell when printing the grid.
border_char
: a char
representing the character to use for the border of the grid.
row_separator_char
: a char
representing the character to use as a separator between rows of the grid.
col_separator_char
: a char
representing the character to use as a separator between columns of the grid.
x_axis_separator_char
: a char
representing the character to use as a separator between the column numbers at the bottom of the grid.
alignment
: an Alignment
value representing the alignment of the cell values when printing the grid.
The draw
method first generates the top and bottom border of the grid using the border_char
and cell_width
. It then creates a template for each row of the grid, which consists of the row number, a row separator, the cell values for that row, and a row values separator.
It then iterates over the rows of the grid, from bottom to top, and generates a string for each row using the template and the current row number and cell values. It then prints each row to the console.
Finally, it generates the column numbers at the bottom of the grid using the alignment
and x_axis_separator_char
, and prints them to the console.
6. The main
function
Copy code
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,
);
}
The main
function creates a new Grid
object with 3 rows and 3 columns, and sets the characters in the cells using the set_cell
method. It then calls the draw
method on the Grid
object to print it to the console, with a cell width of 4, a border character of '+'
, a row separator character of '|'
, a column separator character of ' '
, an x-axis separator character of ' '
, and an alignment of Alignment::Right
.
Step | Description |
---|---|
Define the Grid struct |
The Grid struct represents a grid of characters with three fields: cells , a 2D vector of characters representing the cells in the grid; num_rows , a usize representing the number of rows in the grid; and num_cols , a usize representing the number of columns in the grid. |
Define the Alignment enum |
The Alignment enum represents the possible alignments for the cell values when printing the grid. It has three variants: Left , which aligns the cell value to the left; Center , which aligns the cell value to the center; and Right , which aligns the cell value to the right. |
Define the new method |
The new method is a constructor for the Grid struct. It takes three parameters: num_rows , a usize representing the number of rows in the grid; num_cols , a usize representing the number of columns in the grid; and empty_cell_char , a char representing the character to fill empty cells. It creates a new Grid object with the given number of rows and columns, and fills the cells with the given character. |
Define the set_cell method |
The set_cell method sets the character in a specific cell of the grid. It takes three parameters: row , a usize representing the row number of the cell; col , a usize representing the column number of the cell; and character , a char representing the character to set in the cell. It sets the character in the cell at the given row and column. |
Define the draw method |
The draw method prints the grid to the console, with the given parameters. It takes six parameters: cell_width , a usize representing the width of each cell when printing the grid; border_char , a char representing the character to use for the border of the grid; row_separator_char , a char representing the character to use as a separator between rows of the grid; col_separator_char , a char representing the character to use as a separator between columns of the grid; x_axis_separator_char , a char representing the character to use as a separator between the column numbers at the bottom of the grid; and alignment , an Alignment value representing the alignment of the cell values when printing the grid. It first generates the top and bottom border of the grid using the border_char and cell_width . It then creates a template for each row of the grid, which consists of the row number, a row separator, the cell values for that row, and a row values separator. It then iterates over the rows of the grid, from bottom to top, and generates a string for each row using the template and the current row number and cell values. It then prints each row to the console. Finally, it generates the column numbers at the bottom of the grid using the alignment and x_axis_separator_char , and prints them to the console. |
Define the main function |
The main function creates a new Grid object with 3 rows and 3 columns, and sets the characters in the cells using the set_cell method. It then calls the draw method on the Grid object to print it to the console, with a cell width of 4, a border character of '+' , a row separator character of `' |
Step | Description |
---|---|
Define the Grid struct |
The Grid struct represents a grid of characters with three fields: cells , a 2D vector of characters representing the cells in the grid; num_rows , a usize representing the number of rows in the grid; and num_cols , a usize representing the number of columns in the grid. |
Define the Alignment enum |
The Alignment enum represents the alignment of the cell values when printing the grid. It has three variants: Left , Center , and Right . |
Define the new method |
The new method creates a new Grid object with the given number of rows and columns, and fills the cells with a given character. |
Define the set_cell method |
The set_cell method sets the character in a specific cell of the grid. |
Define the draw method |
The draw method prints the grid to the console with the given parameters. |
Define the main function |
The main function creates a Grid object and sets the characters in its cells. It then prints the grid to the console with the draw method. |
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.