Reputation: 29
I'm new to python. I am trying to write a code to take input from text file like
6 6
* o o o o *
o o * o o o
o o * o o *
o o * o o o
o o o o * o
o o o o o o
and count the number of "*" near each string and update each string with the new count like:
6 6
* 2 1 1 1 *
1 3 * 2 2 2
0 3 * 3 1 *
0 2 * 2 2 2
0 1 1 2 * 1
0 0 0 1 1 1
And update this on an output.txt. Until now my code is taking input and providing rows, column, and matrix but as soon as I get into the list to count, it fails giving error
if matrix[num_rows][num_columns][1] == "x":
IndexError: list index out of range
My code snippet:
def parse_in(input_name):
list_of_lists = []
with open(input_name,"r") as f:
for line in f:
with open(input_name) as f:
num_rows, num_columns = [int(x) for x in next(f).split()]
lines = f.read().splitlines()
# in alternative, if you need to use the file content as numbers
matrix = []
print(lines)
for x in lines:
matrix.append(x.split(' '))
print(matrix)
return matrix, num_rows, num_columns
def detector(matrix, num_rows, num_columns):
mine_count = 0
# For every every space around the square, including itself
for r in range(num_rows):
for c in range(num_columns):
# If the square exist on the matrix
if 0 <= num_rows + r <= 2 and 0 <= num_columns + c <= 2:
# If the square contains a mine
if matrix[r][c] == "*":
# Raise the mine count
mine_count = mine_count + 1
# If the original square contains a mine
if matrix[r][c] == "*":
print(mine_count)
# Lower the mine count by 1, because the square itself having a mine shouldn't be counted
mine_count = mine_count - 1
print(mine_count)
return mine_count
def parse_out(output_name, my_solution):
pass
def my_main(input_name, output_name):
# 1. We do the parseIn from the input file
lines, num_rows, num_columns = parse_in(input_name)
# 2. We do the strategy to solve the problem
my_solution = detector(lines, num_rows, num_columns)
# 3. We do the parse out to the output file
parse_out(output_name, my_solution)
if __name__ == '__main__':
# 1. Name of input and output files
input_name = "input_2.txt"
output_name = "output.txt"
# 2. Main function
my_main(input_name, output_name)
Upvotes: 0
Views: 4422
Reputation: 6935
First read the text file and get the line contents into a numpy array, with this :
with open('test1.txt', 'r') as f:
all_lines = f.readlines()
mat_shape = tuple(map(int, all_lines[0].split()))
lines = [i.strip().split() for i in all_lines[1:]]
lines = np.array(lines)
Read the first line of the text file, split, map them into int and keep it in a tuple as we use it to resize our matrix later.
lines
would be like this :
[['*' 'o' 'o' 'o' 'o' '*']
['o' 'o' '*' 'o' 'o' 'o']
['o' 'o' '*' 'o' 'o' '*']
['o' 'o' '*' 'o' 'o' 'o']
['o' 'o' 'o' 'o' '*' 'o']
['o' 'o' 'o' 'o' 'o' 'o']]
Get the neighbor items for each cell of the matrix, with this function :
def get_neighbours(lines, cell):
row, col = cell
row_max = len(lines)
col_max = len(lines[0])
cell_cont = lines[row][col]
if cell_cont!="*":
return [lines[row_d + row][col_d + col] for col_d in [-1,0,1] if (0 <= (col_d + col) < col_max) or (col_d == 0 and row_d==0) for row_d in [-1,0,1] if 0 <= (row_d + row) < row_max ].count('*')
else:
return '*'
The function takes whole matrix and a particular cell which is a tuple of row and column number. It returns only '*'
if there is a star in the cell, otherwise an integer - the number of stars in the adjacent neighbor cells.
Now create a new array, and call this function for each cell of the matrix :
new = []
for i,_ in enumerate(lines):
for j,_ in enumerate(lines[i]):
new.append(get_neighbours(lines, (i,j)))
new = np.array(new)
If you now reshape this matrix into the desired format by this :
new = new.reshape(mat_shape)
It becomes :
[['*' '2' '1' '1' '1' '*']
['1' '3' '*' '2' '2' '2']
['0' '3' '*' '3' '1' '*']
['0' '2' '*' '3' '2' '2']
['0' '1' '1' '2' '*' '1']
['0' '0' '0' '1' '1' '1']]
You can write this into a new text file with this :
with open('new1.txt', 'w') as f:
f.write(all_lines[0])
for i in new:
f.write(' '.join(i))
f.write('\n')
It would write the following content into the new1.txt
file :
6 6
* 2 1 1 1 *
1 3 * 2 2 2
0 3 * 3 1 *
0 2 * 2 2 2
0 1 1 2 * 1
0 0 0 1 1 1
Upvotes: 1
Reputation: 782529
When creating the matrix, you don't need two loops. You can build the matrix directly in the loop that reads the file. You also don't need to open the file multiple times.
def parse_in(input_name):
matrix = []
with open(input_name,"r") as f:
num_rows, num_columns = [int(x) for x in next(f).split()]
for line in f:
matrix.append(line.split(' '))
return matrix, num_rows, num_columns
You don't need to pass num_rows
and num_columns
to the detector()
function. Unlike languages like C, Python knows the length of lists, so you can just loop over the list elements directly. And you can use enumerate()
to get the indexes as you're looping.
When counting the number of mines next to a square, you just need to loop from r-1
to r+1
and from c-1
to c+1
. And you need to set mine_count
to 0
before this loop.
def detector(matrix):
result = []
for r, row in enumerate(matrix):
result_row = []
for c, cell in enumerate(row):
if cell == "*":
result_row.append(cell)
else:
mine_count = 0
for x in range(c-1, c+2):
for y in range(r-1, r+2):
if 0 <= x < len(row) and 0 <= y < len(matrix) and matrix[x][y] == "*":
mine_count += 1
result_row.append(str(mine_count))
result.append(result_row)
return result
Upvotes: 1