1

Having some issues with a print to .txt function. There are a lot of options available, and I've tried several methods, but I'm running into errors.

I've created a Sudoku solver that reads an amount of 9×9 arrays from a text file and solves all of them. I have a working function that prints them into the terminal, but I need to output the results, formatted as they are in the terminal, into a text file.

Is there someway to amend the print function below to achieve that? I have a semi-working separate function that outputs to a .txt, but in an unformatted method and only one of the four puzzle arrays I've been using.

Thank you for your help.

def print_board(board):
  
  for x in range(len(board)):
    if x % 3 == 0 and x != 0:
        print("- - -   - - -   - - - ")
    for y in range(len(board[0])):
      if y % 3 == 0 and y != 0:
        print("| ", end="")
      if y == 8:
        print(board[x][y])
      else: 
        print(str(board[x][y]) + " ", end="")
  return

This is the export function that is only displaying one array.

def export_board(board):
  
  outputfile=open('solvedpuzzles.txt', 'w')
  print(output, file=outputfile)
  outputfile.close()

This is the format I need to achieve, that is currently working in the terminal from print_board

4 8 3 | 9 2 1 | 6 5 7
9 6 7 | 3 4 5 | 8 2 1
2 5 1 | 8 7 6 | 4 9 3
- - -   - - -   - - -
5 4 8 | 1 3 2 | 9 7 6
7 2 9 | 5 6 4 | 1 3 8
1 3 6 | 7 9 8 | 2 4 5
- - -   - - -   - - -
3 7 2 | 6 8 9 | 5 1 4
8 1 4 | 2 5 3 | 7 6 9
6 9 5 | 4 1 7 | 3 8 2

Edit: I have made some adjustments, and have this:

def print_board(boards):
  
  output = ''

  with open("puzzleoutput.txt", "a") as file:
    for x in range(len(board)):
        if x % 3 == 0 and x != 0:
            output += (str("\n - - -   - - -   - - - "))
        for y in range(len(board[0])):
          if y % 3 == 0 and y != 0:
            output += (str(" | "))
          if y == 8:
            output += (str(board[x][y]))
          else: 
            output += (str(f"{board[x][y]} "))

    file.write(str(output))
    return  

Can someone please let me know how to add a new line for the individual rows of 9? It prints out the array as such, and I think I've nearly cracked it

4 8 3  | 9 2 1  | 6 5 79 6 7  | 3 4 5  | 8 2 12 5 1  | 8 7 6  | 4 9 3
 - - -   - - -   - - - 5 4 8  | 1 3 2  | 9 7 67 2 9  | 5 6 4  | 1 3 8
 - - -   - - -   - - - 3 7 2  | 6 8 9  | 5 1 48 1 4  | 2 5 3  | 7 6 9
Wolf
  • 9,679
  • 7
  • 62
  • 108
Ian
  • 41
  • 3

2 Answers2

0

Could you not simply replace the print() statement with a writelines([]) statement?

output = []

with open("demofile3.txt", "a") as file:
    for x in range(len(board)):
        if x % 3 == 0 and x != 0:
            output.append("- - -   - - -   - - - ")
        for y in range(len(board[0])):
          if y % 3 == 0 and y != 0:
            output.append("| ")
          if y == 8:
            output.append(board[x][y])
          else: 
            output.append(f"{board[x][y]} ")


    file.writelines(output)
  • Thanks Lawrence. I'm very, very new to Python. This is giving me an error however, of:line 91, in print_board output.append(str(board[x][y]) + " ", end="") TypeError: list.append() takes no keyword arguments – Ian Jan 05 '21 at 16:17
  • Hi Ian, sorry I was a little lazy with my copy and pasting, I'd think you could just remove the 'end=""' statement. Let me modify my answer! – Lawrence Bird Jan 05 '21 at 16:32
  • I also changed to using string interpolation; note 'f"{bard[x][y]} "' - its a neater way of including variables in a string if you are running Python3. – Lawrence Bird Jan 05 '21 at 16:34
  • Thank you mate. That's now printing all four boards that have been processed - but unfortunately not in the desired format. It's just printing the arrays flat out, as such: 4 8 3 | 9 2 1 | 6 5 ['4 ', '8 ', '3 ', '| ', '9 ', '2 ', '1 ', '| ', '6 ', '5 ', 7, '9 ', '6 ', '7 ', '| ', '3 ', '4 ', '5 ', '| ', '8 ', '2 ', 1, '2 ', '5 ', '1 ', '| ', '8 ', '7 ', '6 ', '| ', '4 ', '9 ', 3, '- - - - - - - - - ', '5 ', '4 and so on :( I had to amend the file.writelines(output) to file.writelines(str(output)) as it was returning an error message. Any idea how to retain the format? – Ian Jan 06 '21 at 03:03
  • Hey mate. I've edited my post, because I made some changes to the code. I think all I need to do is place each row of the array in its own line, but I don't know where to add the code. Would you happen to know? Thank you – Ian Jan 06 '21 at 10:53
  • Hi mate - I wasn't able to edit my last comment. I used output += (str(' \n ')) to add line breaks where needed, and was able to achieve the formatting. Thank you for your help! – Ian Jan 06 '21 at 11:11
  • Awesome! Well done! Sorry I wasn’t as helpful! Feel free to create your own answer and mark it as solved or I can modify mine to include your new line break :) – Lawrence Bird Jan 06 '21 at 13:22
0

Preface

When I searched the web for a solved Sudoku, which is much harder than searching for Sudokus to solve. Here I found what I was looking for, so thank you for posting one.

That's what I want to give back, be it to you, be it to others who stumble upon this question someday.

Printing to files

The print function that's a Python 3 built in has an optional parameter file, which defaults to sys.stdout for the print calls you already know. The best way to solve your problem is to pass an (again optional) parameter through your print_board function to print. The following script contains a modified version of your function. It not also shows the usage of print function's the file parameter but also removes the flaw of mixing up x and y (if we take x as the horizontal and y as the vertical coordinate) because you are processing rows in the outer loop. This script also contains a function that turns what you define as output into a board (I was too lazy to type a nested python list):

# The optional `output` parameter specifies an 
# open file; leave out to print to the console
def print_board(board, output=None):
  for y in range(len(board)):
    if y % 3 == 0 and y != 0:
        print("- - -   - - -   - - - ", file=output)
    for x in range(len(board[0])):
      if x % 3 == 0 and x != 0:
        print("| ", end="", file=output)
      if x == 8:
        print(board[y][x], file=output)
      else: 
        print(str(board[y][x]) + " ", end="", file=output)

# Read a bord from a string that was formatted by
# the `print_board` function
def text_to_board(text):
    text = text.strip()
    board = list()
    for line in text.split('\n'):
        if '-' not in line:
            row = list()
            for letter in line.split():
                if letter != '|':
                    row.append(letter)
            board.append(row)
    return board

text = """
4 8 3 | 9 2 1 | 6 5 7
9 6 7 | 3 4 5 | 8 2 1
2 5 1 | 8 7 6 | 4 9 3
- - -   - - -   - - -
5 4 8 | 1 3 2 | 9 7 6
7 2 9 | 5 6 4 | 1 3 8
1 3 6 | 7 9 8 | 2 4 5
- - -   - - -   - - -
3 7 2 | 6 8 9 | 5 1 4
8 1 4 | 2 5 3 | 7 6 9
6 9 5 | 4 1 7 | 3 8 2
"""

board = text_to_board(text)
print_board(board)

with open('puzzleoutput.txt', 'w') as f: 
    print_board(board, f)

Performance considerations

Normally it's efficient to directly writing to file, I experienced it to be fast enough. So I'd not suggest build a text buffer that's written at once: String concatenation, especially if done for character by character, involves a lot of allocating and freeing memory operations.

If performance really matters, I'd recommend a redesign not only the print_board function but also of the board storage itself. And of course, a solid knowledge about how to measure performance.

Further reading

print to file

Wolf
  • 9,679
  • 7
  • 62
  • 108