#!/usr/bin/env python
"""Sudoku puzzle downloader and printer. Uses puzzles from websudoku.com"""
#Copyright (C) 2008 Patrick Tait
#
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program. If not, see .
import urllib
from psxml import *
import string
import time
import sys
FourWideCell = True #3 spaces + charictor, or 2 spaces + charictor.
#4 looks better with 80 char width printer, 3 looks better
#with 40 charicter width printer
def GetPuzzle(Level):
"""Downloads the puzzle page, scrapes the HTML for the needed information.
returns a 2d list with empty cells noted with a " "."""
puzzle = urllib.urlopen("http://show.websudoku.com/?level=%s" % Level)
XMLRAW = Parse(puzzle.read())
XML = [ii for ii in XMLRAW]#psxml is meant to be a stream parser,
#this lets you access the entire page.
Puzzle = []
FormStart = -1
FormEnd = len(XML)
TableStart = -1
TableEnd = -1
#find the range of the form. The relevant form name is board.
for ii in range(len(XML)):
if XML[ii].__class__ is EndTag and XML[ii] == "FORM":
FormEnd = ii
elif XML[ii] == "FORM":
if "name" in XML[ii].Attr.keys() and \
XML[ii].Attr["name"] == "board":
FormStart = ii
if FormStart == -1:
print "Coudn't find forum start" # XXX
return False
#find the range of the table
for ii in range(FormStart, FormEnd):
if XML[ii] == "TABLE":
if XML[ii].__class__ is StartTagAttr or \
XML[ii].__class__ is StartTag:
TableStart = ii
elif XML[ii].__class__ is EndTag:
TableEnd = ii
if TableStart == -1 or TableEnd == -1:
print "Coudn't find table start" # XXX
return False
#parse the data. The values are in "INPUT" tags.
for ii in XML[TableStart:TableEnd]:
if ii == "TR" and ii.__class__ is not EndTag:
Puzzle.append([])
elif ii == "INPUT":
if "value" in ii.Attr.keys():
Puzzle[-1].append(ii.Attr["value"])
else:
Puzzle[-1].append(" ")
return Puzzle
def DisplayPuzzle(Puzzle):
"""Quick debug function to print puzzles. Blank cells have an X"""
for ii in Puzzle:
for jj in ii:
if jj == " ":
jj = "X"
print " %s" % jj,
print "\n",
term = 0
printer = 1
def PrettyPuzzle(Puzzle, Set = term):
"""Formats the puzzle into printable format. Set determines the charicter set
used for printing. term is for terminal output, printer is for the DPU-411
printer with the international switches set XXX. """
if Set == printer:
norm = string.maketrans('', '')
TLcorner = norm[152] #Top left corner
TRcorner = norm[153] #Top right corner
Tdiv = norm[149] #Top horizontal division
TCol = norm[145] #Top columnular division
BLcorner = norm[154] #Bottom Left corner
BRcorner = norm[155] #Bottom Right corner
Bdiv = norm[149] #Bottom horizontal division
BCol = norm[144] #Bottom columnular division
LBorder = norm[150] #Left vertical border
LRow = norm[147] #Left row border
RBorder = norm[150] #Right vertical border
RRow = norm[146] #Right row border
SVdiv = norm[135] #Region vertical division
SHdiv = norm[135] #Region horizontal division
SCdiv = norm[135] #Region Central division
CVdiv = norm[150] #Cell Vertical division
CHdiv = norm[149] #Cell horizontal division
CCdiv = norm[143] #Cell central division
elif Set == term:
TLcorner = '+' #Top left corner
TRcorner = '+' #Top right corner
Tdiv = '-' #Top horizontal division
TCol = '-' #Top columnular division
BLcorner = '+' #Bottom Left corner
BRcorner = '+' #Bottom Right corner
Bdiv = '-' #Bottom horizontal division
BCol = '-' #Bottom columnular division
LBorder = '|' #Left vertical border
LRow = '|' #Left row border
RBorder = '|' #Right vertical border
RRow = '|' #Right row border
SVdiv = '#' #Region vertical division
SHdiv = '#' #Region horizontal division
SCdiv = '#' #Region Central division
CVdiv = '|' #Cell Vertical division
CHdiv = '-' #Cell horizontal division
CCdiv = '+' #Cell central division
Output = ""
Rows = 9
Cols = 9
CurRow = 1
CurCol = 1
#Outputs the top row
Output += TLcorner
for ii in range(Cols):
Output += Tdiv
if FourWideCell:
Output += Tdiv
Output += Tdiv
Output += Tdiv
Output += TCol
Output = Output[0:-1] + TRcorner
Output += "\n"
#step through the puzzle's rows
for ii in Puzzle:
Output += LBorder
CurCol = 1
#step through the cells in a row
for jj in ii:
Output += " "
Output += jj
Output += " "
if FourWideCell:
Output += " "
if CurCol % 3 == 0: #major devision, between regions
Output += SVdiv
else: #minor devision
Output += CVdiv
CurCol += 1
Output = Output[0:-1] + RBorder
Output += "\n"
#Output a column divider
if CurRow == Rows: #Last row
Output += BLcorner #Bottom Row
for ii in range(Cols):
Output += Bdiv
Output += Bdiv
if FourWideCell:
Output += Bdiv
Output += Bdiv
Output += BCol
Output = Output[0:-1] + BRcorner
Output += "\n"
elif CurRow % 3 == 0: #region divider
Output += LRow
for jj in range(Cols):
Output += SHdiv
Output += SHdiv
if FourWideCell:
Output += SHdiv
Output += SHdiv
Output += SCdiv
Output = Output[0:-1] + RRow
Output += "\n"
else:
Output += LRow
for jj in range(Cols):
Output += CHdiv
if FourWideCell:
Output += CHdiv
Output += CHdiv
Output += CHdiv
if (jj + 1) % 3 == 0:
Output += SCdiv
else:
Output += CCdiv
Output = Output[0:-1] + RRow
Output += "\n"
CurRow += 1
return Output
def SerPrint(Puzzle, port = 0):
"""prints the puzzle to a serial port, with a 2.5 second delay between lines.
Meant to allow printing to the "DPU-411" printer. """
import serial
Ser = serial.Serial(port)
Ser.write(" .\n")
time.sleep(2.5)
Ser.flush()
Output = PrettyPuzzle(Puzzle, printer)
for ii in Output.split("\n"):
Ser.write(ii + "\n")
Ser.flush()
time.sleep(2.5)
Ser.write("\n\n\n")
Ser.write("\n\n\n")
Ser.close()
if __name__ == "__main__":
if len(sys.argv) > 1:
Diff = sys.argv[1]
if len(sys.argv) > 2:
Port = int(sys.argv[2])
SerPrint(GetPuzzle(Diff), Port)
else:
print PrettyPuzzle(GetPuzzle(Diff), Set = term)
else:
print \
"""Usage: sudoku.py ,
If no serial port is specified, it prints to stout. """