Weird Sudoku validation bug - python

I'm currently trying to program a recursive sudoku solving algorithm in Python. I made a class for Sudokus which contains some methods to help me manipulate the sudoku grid.
Here is my code :
class Sudoku:
def __init__(self, input, tokens = None):
self.data = {}
if tokens is None:
self.tokens = list(range(1, 10))
else:
self.tokens = tokens
assert len(self.tokens) == 9
if type(input) == dict:
self.data = input
else:
for i, cell in enumerate(input):
if cell in self.tokens:
self.data[i % 9, i // 9] = cell
def __repr__(self):
string = ''
canvas = [['.'] * 9 for line in range(9)]
for (col, row), cell in self.data.items():
canvas[row][col] = str(cell)
for y, row in enumerate(canvas):
if not y % 3:
string += "+-------+-------+-------+\n"
string += '| {} | {} | {} |\n'.format(' '.join(row[:3]), ' '.join(row[3:6]), ' '.join(row[6:]))
string += "+-------+-------+-------+"
return string
#classmethod
def sq_coords(cls, cell_x, cell_y):
#returns all coordinates of cells in the same square as the one in (cell_x, cell_y)
start_x, start_y = cell_x // 3 * 3, cell_y // 3 * 3
for dx in range(3):
for dy in range(3):
yield (start_x +dx, start_y + dy)
def copy(self):
return Sudoku(self.data)
def clues(self, cell_x, cell_y):
assert not self.data.get((cell_x, cell_y))
allowed = set(self.tokens)
#Remove all numbers on the same row, column and square as the cell
for row in range(9):
allowed.discard(self.data.get((cell_x, row)))
for col in range(9):
allowed.discard(self.data.get((col, cell_y)))
for coords in self.sq_coords(cell_x, cell_y):
allowed.discard(self.data.get(coords))
return allowed
def get_all_clues(self):
clues = {}
for row in range(9):
for col in range(9):
if not self.data.get((col, row)):
clues[col, row] = self.clues(col, row)
return clues
def fill_singles(self):
still_going = True
did_something = False
while still_going:
still_going = False
for (col, row), clues in self.get_all_clues().items():
if len(clues) == 1:
still_going = True
did_something = True
self.data[col, row] = clues.pop()
return did_something
def place_finding(self):
still_going = True
did_something = False
while still_going:
still_going = False
for token in self.tokens:
for group in self.get_groups():
available_spots = [coords for coords, cell in group.items() if cell == None and token in self.clues(*coords)]
if len(available_spots) == 1:
self.data[available_spots.pop()] = token
still_going = True
did_something = True
return did_something
def fill_obvious(self):
still_going = True
while still_going:
a = self.fill_singles()
b = self.place_finding()
still_going = a or b
def get_groups(self):
for y in range(9):
yield {(x, y) : self.data.get((x, y)) for x in range(9)}
for x in range(9):
yield {(x, y) : self.data.get((x, y)) for y in range(9)}
for n in range(9):
start_x, start_y = n % 3 * 3, n // 3 * 3
yield {(x, y) : self.data.get((x, y)) for x, y in self.sq_coords(start_x, start_y)}
def is_valid(self):
for group in self.get_groups():
if any([list(group.values()).count(token) > 1 for token in self.tokens]):
return False
return True
def is_solved(self):
return self.is_valid() and len(self.data) == 9 * 9
def solve(sudoku):
def loop(su):
if su.is_solved():
print(su)
elif su.is_valid():
su.fill_obvious()
print(su)
for coords, available_tokens in sorted(su.get_all_clues().items(), key=lambda kv: len(kv[1])):
for token in available_tokens:
new_su = su.copy()
new_su.data[coords] = token
loop(new_su)
loop(sudoku)
with open('input.txt') as f:
numbers = ''
for i, line in enumerate(f):
if i >= 9:
break
numbers += line.rstrip().ljust(9)
s = Sudoku(numbers, tokens='123456789')
print(s)
solve(s)
print(s)
Sorry if this seems messy but I'd rather give you everything I have than only some data that may or may not contain the problem.
As you can see, the first thing it does is filling the Sudoku with only 100% sure numbers using the methods fill_singles (fills every cell that can only be filled with number x, eg the 8 other possibilities are in its row, column or block) and place_finding (checks all tokens and see if there's only one space in a group - row, column or block - where they can fit). It loops through both of those until nothing can be done.
Afterwards, it tries every possibility in spaces that have least and tries to solves the newly made grid with the same method. That's where my problem is. Currently, for debug purposes, the program prints grids it comes across whenever they're valid (no same number twice in a group). Actually that's what I'd like it to do. However it doesn't work like this; with this input :
+-------+-------+-------+
| . . . | . 2 6 | . . 4 |
| . . . | 7 9 . | 5 . . |
| . . . | . . . | 9 1 . |
+-------+-------+-------+
| . 8 . | 1 . . | . . . |
| 2 3 6 | . . . | 1 8 5 |
| . . . | . . 3 | . 7 . |
+-------+-------+-------+
| . 4 7 | . . . | . . . |
| . . 3 | . 7 8 | . . . |
| 5 . . | 6 3 . | . . . |
+-------+-------+-------+
It outputs grids such as this one, which is obviously not valid:
+-------+-------+-------+
| 1 4 9 | 3 2 6 | 7 3 4 |
| 3 2 4 | 7 9 1 | 5 1 8 |
| 3 7 5 | 3 2 4 | 9 1 2 |
+-------+-------+-------+
| 7 8 6 | 1 1 4 | 3 2 5 |
| 2 3 6 | 9 4 7 | 1 8 5 |
| 4 1 7 | 2 3 3 | 6 7 4 |
+-------+-------+-------+
| 2 4 7 | 1 1 3 | 5 4 6 |
| 1 3 3 | 4 7 8 | 2 5 1 |
| 5 5 4 | 6 3 2 | 1 3 7 |
+-------+-------+-------+
I really cannot understand why it allows such grids to pass the is_valid test, especially considering that when I manually input the grid above, it doesn't pass the test :
>>> s
+-------+-------+-------+
| 1 4 9 | 3 2 6 | 7 3 4 |
| 3 2 4 | 7 9 1 | 5 1 8 |
| 3 7 5 | 3 2 4 | 9 1 2 |
+-------+-------+-------+
| 7 8 6 | 1 1 4 | 3 2 5 |
| 2 3 6 | 9 4 7 | 1 8 5 |
| 4 1 7 | 2 3 3 | 6 7 4 |
+-------+-------+-------+
| 2 4 7 | 1 1 3 | 5 4 6 |
| 1 3 3 | 4 7 8 | 2 5 1 |
| 5 5 4 | 6 3 2 | 1 3 7 |
+-------+-------+-------+
>>> s.is_valid()
False
Can anyone see an error in my code that I haven't noticed? I'm sorry I'm not really being specific but I tried looking through every piece of my code and can't seem to find anything.
For #AnandSKatum :
26 4
79 5
91
8 1
236 185
3 7
47
3 78
5 63

here is a better implementation that I wrote for you
from numpy.lib.stride_tricks import as_strided
from itertools import chain
import numpy
def block_view(A, block= (3, 3)):
"""Provide a 2D block view to 2D array. No error checking made.
Therefore meaningful (as implemented) only for blocks strictly
compatible with the shape of A."""
# simple shape and strides computations may seem at first strange
# unless one is able to recognize the 'tuple additions' involved ;-)
shape= (A.shape[0]/ block[0], A.shape[1]/ block[1])+ block
strides= (block[0]* A.strides[0], block[1]* A.strides[1])+ A.strides
return chain.from_iterable(as_strided(A, shape= shape, strides= strides))
def check_board(a):
"""
a is a 2d 9x9 numpy array, 0 represents None
"""
for row,col,section in zip(a,a.T,block_view(a,(3,3))):
s = list(chain.from_iterable(section))
if any(sum(set(x)) != sum(x) for x in [row,col,s]):
return False
return True
a = numpy.array(
[
[9,8,7,6,5,4,3,2,1],
[2,1,5,0,0,0,0,0,0],
[3,6,4,0,0,0,0,0,0],
[4,2,0,0,0,0,0,0,0],
[5,3,0,0,0,0,0,0,0],
[6,7,0,0,0,0,0,0,0],
[7,0,0,0,0,0,0,0,0],
[8,0,0,0,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0],
]
)
print check_board(a)

Related

How to get my sudoku solver to read from text file in python?

I am having trouble figuring out how to read from a text file for my Sudoku solver. So I can get it to read the board from a text file, and I can get my code to solve the board when it's in the code, but I can't bet them to read and solve together. This is what I have and I've tried a few methods to get it to possibly work, but I'm not understanding it. If anyone could help, I'd really appreciate it! Thanks!
board = []
with open('project.txt','r') as file:
for line in file:
board.append(line.strip('\n').split(','))
if line != '':
(board)
#backtracking
def solve(pr):
find = find_empty(pr)
if not find:
return True
else:
row, col = find
for r in range(1,10):
if valid(pr, r, (row, col)):
pr[row][col] = r
if solve(pr):
return True
pr[row][col] = 0
return False
def valid(pr, num, pos):
# Check row
for r in range(len(pr[0])):
if pr[pos[0]][r] == num and pos[1] != r:
return False
# Check column
for r in range(len(pr)):
if pr[r][pos[1]] == num and pos[0] != r:
return False
# Check box
box_x = pos[1] // 3
box_y = pos[0] // 3
for r in range(box_y*3, box_y*3 + 3):
for c in range(box_x * 3, box_x*3 + 3):
if pr[r][c] == num and (r,c) != pos:
return False
return True
#formatting
def print_board(pr):
for r in range(len(pr)):
if r % 3 == 0 and r != 0:
print("- - - - - - - - - - - - - ")
for c in range(len(pr[0])):
if c % 3 == 0 and c != 0:
print(" | ", end="")
if c == 8:
print(pr[r][c])
else:
print(str(pr[r][c]) + " ", end="")
def find_empty(pr):
for r in range(len(pr)):
for c in range(len(pr[0])):
if pr[r][c] == 0:
return (r, c) # row, col
return None
solve(board)
print_board(board)
my txt file looks like this:
003020600
900305001
001806400
008102900
700000008
006708200
002609500
800203009
005010300
With this one-line change, your code solves the sudoku, even after I fill in 0s for the missing row.
board = []
with open('project.txt','r') as file:
for line in file:
board.append(list(map(int,line.strip())))
I added a print_board before solving. That's why you see it twice.
Output:
9 0 0 | 3 0 5 | 0 0 1
0 0 1 | 8 0 6 | 4 0 0
0 0 8 | 1 0 2 | 9 0 0
- - - - - - - - - - - - -
7 0 0 | 0 0 0 | 0 0 8
0 0 6 | 7 0 8 | 2 0 0
0 0 2 | 6 0 9 | 5 0 0
- - - - - - - - - - - - -
8 0 0 | 2 0 3 | 0 0 9
0 0 5 | 0 1 0 | 3 0 0
0 0 0 | 0 0 0 | 0 0 0
9 2 4 | 3 7 5 | 8 6 1
5 3 1 | 8 9 6 | 4 7 2
6 7 8 | 1 4 2 | 9 3 5
- - - - - - - - - - - - -
7 5 3 | 4 2 1 | 6 9 8
1 9 6 | 7 5 8 | 2 4 3
4 8 2 | 6 3 9 | 5 1 7
- - - - - - - - - - - - -
8 4 7 | 2 6 3 | 1 5 9
2 6 5 | 9 1 7 | 3 8 4
3 1 9 | 5 8 4 | 7 2 6
I would do something like this.
def loadBoard(fname):
board = []
with open(fname, 'r') as f:
for line in f:
board.append([int(x) for x in line.strip('\n')])
return board
This is based on the what you said the input is in your comment to Mike_S's answer, in other words just rows of numbers with no separators (unless you include the newlines of course). It seems to me like you're using the input as ints, so you would want to convert them to ints as I do in this example. Then to get the board, you just do:
board = loadBoard('project.txt')
I hope this helps. If I misunderstood something and you let me know, I will do my best to come back and correct it.
Edited because I forgot to put
board = []
inside the function.

How to print Sudoku grid more efficiently

I'm Programming Sudoku game in terminal, I want to Print the grid to console with a square around it as in the picture below.
There is no problem with my code except it is inefficient.
I would like to get it more efficient and short (with list comprehensions, string multiplying, etc).
the board is defined like that,
board = [[_ for _ in range(9)] for _ in range(9)]
That is the function i'm using:
def Print_Board(board):
print("\n-------------------------")
for i in range(9):
for j in range(9):
if board[i][j] is not None:
if j == 0:
print("|", end=" ")
print(f"{board[i][j]} ", end="")
if (j + 1) % 3 == 0:
print("|", end=" ")
if (i + 1) % 3 == 0:
print("\n-------------------------", end=" ")
print()
You can build a board format and throw all the data at it:
bar = '-------------------------\n'
lnf = '|' +(' {:}'*3 + ' |')*3 + '\n'
bft = bar + (lnf*3+bar)*3
print(bft.format(*(el for rw in board for el in rw)))
You only need to build the format once, of course. After that it's just a print.
Suggestion from JonSG in comments to encapsulate this in a closure:
def make_board_printer():
bar = '-------------------------\n'
lnf = '|' +(' {:}'*3 + ' |')*3 + '\n'
bft = bar + (lnf*3+bar)*3
return (lambda bd:print(bft.format(*(el for rw in bd for el in rw))))
is a function which returns a board printer function:
# make a printer:
b_print = make_board_printer()
# then as needed
b_print(board)
You could do this:
def print_board(board):
print("\n-------------------------")
for row_num, row in enumerate(board):
print("|", end=" ")
for col_num, item in enumerate(row):
print(item, end=" ")
if (col_num + 1) % 3 == 0:
print("|", end=" ")
if (row_num + 1) % 3 == 0:
print("\n-------------------------", end=" ")
print()
if __name__ == '__main__':
print_board(board=[range(9) for _ in range(9)])
Here is a (not more readable) solution for your problem:
board = [range(1,10) for i in range(9)]
print("-"*25)
for idx, row in enumerate(board):
rowStr = " | ".join([" ".join(map(str, row[i:i+3])) for i in range(0, len(row), 3)])
print(f'| {rowStr} |')
if (idx+1) % 3 == 0:
print("-"*25)
Output:
-------------------------
| 1 2 3 | 4 5 6 | 7 8 9 |
| 1 2 3 | 4 5 6 | 7 8 9 |
| 1 2 3 | 4 5 6 | 7 8 9 |
-------------------------
| 1 2 3 | 4 5 6 | 7 8 9 |
| 1 2 3 | 4 5 6 | 7 8 9 |
| 1 2 3 | 4 5 6 | 7 8 9 |
-------------------------
| 1 2 3 | 4 5 6 | 7 8 9 |
| 1 2 3 | 4 5 6 | 7 8 9 |
| 1 2 3 | 4 5 6 | 7 8 9 |
-------------------------

Summarize data in a list in python

In python i need to summarize data in count_list this way (like a histogram):
"""
number | occurence
0 | *
1 | **
2 | ***
3 | **
4 | **
5 | *
6 | *
7 | **
8 | ***
9 | *
10 | **
"""
But instead I get this wrong output:
"""
number | occurence
0 |
1 | **
2 |
3 |
4 |
5 |
6 | **
7 |
8 |
9 |
10 | **
"""
Here is my code:
import random
random_list = []
list_length = 20
while len(random_list) < list_length:
random_list.append(random.randint(0,10))
count_list = [0] * 11
index = 0
while index < len(random_list):
number = random_list[index]
count_list[number] = count_list[number] + 1
index = index + 1
def summerizer():
index = 0
print count_list
print '"'*3
print 'number | occurrence'
while index < len(count_list):
print '%s' %' '*(7),
print index,#the problem is here
print ' | ',#and here
print '%s' %'*'*(count_list[index])
index += 1
print '%s'%'"'*3
summerizer()
This method uses collections.Counter:
from collections import Counter
import random
random_list = []
list_length = 20
while len(random_list) < list_length:
random_list.append(random.randint(0,10))
c = Counter(random_list)
print('number | occurrence')
def summerizer(dic):
for v,d in dic.items():
print(v, '|', '%s'%'*'*c[v])
summerizer(dic)
Yes i have found the problem
It is from the ide itself !!!
This was a quiz in a course on UDACITY android application and the embedded compiler inside it make this wrong answer..
Same code i tried now from pydroid application on Android also made the answer that i need without any change
Thanks for trying to help all of you
`import random
random_list = []
list_length = 20
while len(random_list) < list_length:
random_list.append(random.randint(0,10))
count_list = [0] * 11
index = 0
while index < len(random_list):
number = random_list[index]
count_list[number] = count_list[number] + 1
index = index + 1
def summerizer():
index = 0
print count_list
print '"'*3
print 'number | occurrence'
while index < len(count_list):
print '%s' %' '*(7),
print index,
print ' | ',
print '%s' %'*'*(count_list[index])
index += 1
print '%s'%'"'*3
summerizer()`
Try this
import random
random_list = []
list_length = 20
while len(random_list) < list_length:
random_list.append(random.randint(0,10))
dic={}
for i in random_list:
dic[i]=dic.get(i,0)+1
print 'number | occurrence'
for i in range(0,11):
if(i not in dic):
print i,"|",'%s' %'*'*(0)
else:
print i,"|",'%s' %'*'*(dic[i])
Out put
[9, 8, 4, 2, 5, 4, 8, 3, 5, 6, 9, 5, 3, 8, 6, 2, 10, 10, 8, 9]
number | occurrence
0 |
1 |
2 | **
3 | **
4 | **
5 | ***
6 | **
7 |
8 | ****
9 | ***
10 | **

Gurobi: How can I sum just a part of a variable?

I have the following model:
from gurobipy import *
n_units = 1
n_periods = 3
n_ageclasses = 4
units = range(1,n_units+1)
periods = range(1,n_periods+1)
periods_plus1 = periods[:]
periods_plus1.append(max(periods_plus1)+1)
ageclasses = range(1,n_ageclasses+1)
nothickets = ageclasses[1:]
model = Model('MPPM')
HARVEST = model.addVars(units, periods, nothickets, vtype=GRB.INTEGER, name="HARVEST")
FOREST = model.addVars(units, periods_plus1, ageclasses, vtype=GRB.INTEGER, name="FOREST")
model.addConstrs((quicksum(HARVEST[(k+1), (t+1), nothicket] for k in range(n_units) for t in range(n_periods) for nothicket in nothickets) == FOREST[unit, period+1, 1] for unit in units for period in periods if period < max(periods_plus1)), name="A_Thicket")
I have a problem with formulating the constraint. I want for every unit and every period to sum the nothickets part of the variable HARVEST. Concretely I want xk=1,t=1,2 + xk=1,t=1,3 + xk=1,t=1,4
and so on. This should result in only three ones per row of the constraint matrix. But with the formulation above I get 9 ones.
I tried to use a for loop outside of the sum, but this results in another problem:
for k in range(n_units):
for t in range(n_periods):
model.addConstrs((quicksum(HARVEST[(k+1), (t+1), nothicket] for nothicket in nothickets) == FOREST[unit,period+1, 1] for unit in units for period in periods if period < max(periods_plus1)), name="A_Thicket")
With this formulation I get this matrix:
constraint matrix
But what I want is:
row_idx | col_idx | coeff
0 | 0 | 1
0 | 1 | 1
0 | 2 | 1
0 | 13 | -1
1 | 3 | 1
1 | 4 | 1
1 | 5 | 1
1 | 17 | -1
2 | 6 | 1
2 | 7 | 1
2 | 8 | 1
2 | 21 | -1
Can anybody please help me to reformulate this constraint?
This worked for me:
model.addConstrs((HARVEST.sum(unit, period, '*') == ...

Does anyone know how to return a grid into the shell based on a txt file?

Iam trying to make a small interior designing app, I type a txt file and make my program return the grid on shell.
I just need to know how to make a grid where the height and width are both 20.
These are the codes I have so far.. I only know how to make the width but not the height. I also don't know how to get the numbers and letters from my txt file but i made my txt file into a list line by line.
f = open('Apt_3_4554_Hastings_Coq.txt','r')
bigListA = [ line.strip().split(',') for line in f ]
offset = " "
width = 20
string1 = offset
for number in range(width):
if len(str(number)) == 1:
string1 += " " + str(number) + " "
else:
string1 += str(number) + " "
print (string1)
A bit of an overkill, but it was fun to make a class around it:
def decimal_string(number, before=True):
"""
Convert a number between 0 and 99 to a space padded string.
Parameters
----------
number: int
The number to convert to string.
before: bool
Whether to place the spaces before or after the nmuber.
Examples
--------
>>> decimal_string(1)
' 1'
>>> decimal_string(1, False)
'1 '
>>> decimal_string(10)
'10'
>>> decimal_string(10, False)
'10'
"""
number = int(number)%100
if number < 10:
if before:
return ' ' + str(number)
else:
return str(number) + ' '
else:
return str(number)
class Grid(object):
def __init__(self, doc=None, shape=(10,10)):
"""
Create new grid object from a given file or with a given shape.
Parameters
----------
doc: file, None
The name of the file from where to read the data.
shape: (int, int), (10, 10)
The shape to use if no `doc` is provided.
"""
if doc is not None:
self.readfile(doc)
else:
self.empty_grid(shape)
def __repr__(self):
"""
Representation method.
"""
# first lines
# 0 1 2 3 4 5 6 7 ...
# - - - - - - - - ...
number_line = ' '
traces_line = ' '
for i in range(len(self.grid)):
number_line += decimal_string(i) + ' '
traces_line += ' - '
lines = ''
for j in range(len(self.grid[0])):
line = decimal_string(j, False) + '|'
for i in range(len(self.grid)):
line += ' ' + self.grid[i][j] + ' '
lines += line + '|\n'
return '\n'.join((number_line, traces_line, lines[:-1], traces_line))
def readfile(self, doc):
"""
Read instructions from a file, overwriting current grid.
"""
with open(doc, 'r') as open_doc:
lines = open_doc.readlines()
shape = lines[0].split(' ')[-2:]
# grabs the first line (line[0]),
# splits the line into pieces by the ' ' symbol
# grabs the last two of them ([-2:])
shape = (int(shape[0]), int(shape[1]))
# and turns them into numbers (np.array(..., dtype=int))
self.empty_grid(shape=shape)
for instruction in lines[1:]:
self.add_pieces(*self._parse(instruction))
def empty_grid(self, shape=None):
"""
Empty grid, changing the shape to the new one, if provided.
"""
if shape is None:
# retain current shape
shape = (len(self.grid), len(self.grid[0]))
self.grid = [[' ' for i in range(shape[0])]
for j in range(shape[1])]
def _parse(self, instruction):
"""
Parse string instructions in the shape:
"C 5 6 13 13"
where the first element is the charachter,
the second and third elements are the vertical indexes
and the fourth and fifth are the horizontal indexes
"""
pieces = instruction.split(' ')
char = pieces[0]
y_start = int(pieces[1])
y_stop = int(pieces[2])
x_start = int(pieces[3])
x_stop = int(pieces[4])
return char, y_start, y_stop, x_start, x_stop
def add_pieces(self, char, y_start, y_stop, x_start, x_stop):
"""
Add a piece to the current grid.
Parameters
----------
char: str
The char to place in the grid.
y_start: int
Vertical start index.
y_stop: int
Vertical stop index.
x_start: int
Horizontal start index.
x_stop: int
Horizontal stop index.
Examples
--------
>>> b = Grid(shape=(4, 4))
>>> b
0 1 2 3
- - - -
0 | |
1 | |
2 | |
3 | |
- - - -
>>> b.add_pieces('a', 0, 1, 0, 0)
>>> b
0 1 2 3
- - - -
0 | a |
1 | a |
2 | |
3 | |
- - - -
>>> b.add_pieces('b', 3, 3, 2, 3)
>>> b
0 1 2 3
- - - -
0 | a |
1 | a |
2 | |
3 | b b |
- - - -
"""
assert y_start <= y_stop < len(self.grid[0]),\
"Vertical index out of bounds."
assert x_start <= x_stop < len(self.grid),\
"Horizontal index out of bounds."
for i in range(x_start, x_stop+1):
for j in range(y_start, y_stop+1):
self.grid[i][j] = char
You can then have file.txt with:
20 20
C 5 6 13 13
C 8 9 13 13
C 5 6 18 18
C 8 9 18 18
C 3 3 15 16
C 11 11 15 16
E 2 3 3 6
S 17 18 2 7
t 14 15 3 6
T 4 10 14 17
and make:
>>> a = Grid('file.txt')
>>> a
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
- - - - - - - - - - - - - - - - - - - -
0 | |
1 | |
2 | E E E E |
3 | E E E E C C |
4 | T T T T |
5 | C T T T T C |
6 | C T T T T C |
7 | T T T T |
8 | C T T T T C |
9 | C T T T T C |
10| T T T T |
11| C C |
12| |
13| |
14| t t t t |
15| t t t t |
16| |
17| S S S S S S |
18| S S S S S S |
19| |
- - - - - - - - - - - - - - - - - - - -
You can represent the room as a 20x20 grid. One idea is a list of lists; personally I prefer a dict.
Read through the file and assign each point. (I assume you've handled parsing the file, since that wasn't the issue you posted.) For instance:
room[5, 13] = 'C'
Then you can iterate over the coordinates to provide your output.
for i in range(N_ROWS):
for j in range(N_COLS):
# Print the character if it exists, or a blank space.
print(room.get((i, j), default=' '), end='')
print() # Start a new line.

Categories