Guest User

Untitled

a guest
Jul 18th, 2018
177
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.61 KB | None | 0 0
  1. #!/usr/bin/python
  2. #
  3. # Needs python 2.5 for the Element Tree module.
  4.  
  5. from xml.etree import ElementTree
  6. import math
  7. from optparse import OptionParser
  8.  
  9. def pkg_boilerplate(package, version):
  10. """Generate the repetitive stuff at the front of a Package stanza
  11. that's needed in order to create a real Packages file that apt will
  12. parse (well enough for us to run dependency resolution, anyway)."""
  13.  
  14. return '''Package: %(package)s
  15. Version: %(version)s
  16. Filename: %(package)s_%(version)s_all.deb
  17. Architecture: all
  18. Maintainer: Nobody <nobody@nowhere.org>
  19. Installed-Size: 16''' % { 'package' : package, 'version' : version }
  20.  
  21. def main():
  22. parser = OptionParser(usage = '%prog [OPTIONS] (N | FILE)',
  23. epilog='''Build a Debian Packages file corresponding to a Sudoku board. Given
  24. N, build a Packages file for an empty, order-N Sudoku board (the board
  25. size will be N*N); given FILE, build a Packages file for the given
  26. ksudoku save file.''')
  27.  
  28. parser.add_option('-m', '--mode', dest='mode',
  29. metavar='MODE', default='depends',
  30. help='''Set the program mode. MODE may be "print" to display the board
  31. instead of generating a Packages file, "depends" to print a
  32. Packages file using a Depends-based reduction (default), or
  33. "conflicts" to print a Packages file using a Conflicts-based
  34. reduction.''')
  35.  
  36. (options, args) = parser.parse_args()
  37.  
  38. if len(args) == 0:
  39. raise Exception('You must provide at least one input file or board size.')
  40.  
  41. for f in args:
  42. try:
  43. f = int(f)
  44. except:
  45. pass
  46. s = Sudoku(f)
  47. if options.mode == 'depends':
  48. print s.deb_render_depends()
  49. elif options.mode == 'conflicts':
  50. print s.deb_render_conflicts()
  51. elif options.mode == 'print':
  52. print s
  53. else:
  54. raise Exception('Unknown program mode "%s"' % args.mode)
  55.  
  56. class Sudoku:
  57. '''A class that can represent a sudoku game and convert it into a
  58. collection of Debian package relationships.
  59.  
  60. The game is represented as a list of N columns, each containing N
  61. cells. Cells contain a number from 0 to N, inclusive, with 0
  62. representing an empty cell.'''
  63.  
  64. def __init__(self, f = None):
  65. '''Load a ksudoku save file into this Sudoku instance.'''
  66. if isinstance(f, int):
  67. self.N = f
  68. self.board = [[0] * self.N * self.N] * self.N * self.N
  69. else:
  70. e = ElementTree.ElementTree(None, f)
  71. for game in e.findall('game'):
  72. for puzzle in game.findall('puzzle'):
  73. graph = puzzle.find('graph')
  74. order = int(graph.get('order'))
  75. self.N = int(round(math.sqrt(order)))
  76. if self.N * self.N <> order:
  77. raise Exception('The order of a Sudoku puzzle should be a perfect square.')
  78.  
  79. board = puzzle.findtext('values').strip()
  80. if len(board) <> order * order:
  81. raise Exception('Wrong board length.')
  82.  
  83. self.board = []
  84. for col in range(0, order):
  85. col_values = []
  86. for row in range(0, order):
  87. cell = board[col * order + row]
  88. if cell == '_':
  89. col_values.append(0)
  90. else:
  91. col_values.append(ord(cell) - ord('a'))
  92.  
  93. self.board.append(col_values)
  94.  
  95. def flatten(self):
  96. '''Return an iterable that gives the values of this Sudoku
  97. board as a series of (row, column, value) triples, with row
  98. and column being zero-based.'''
  99. for row in range(0, self.N * self.N):
  100. for col in range(0, self.N * self.N):
  101. yield(row, col, self.board[col][row])
  102.  
  103. def __str__(self):
  104. """Print a human-readable representation of the board."""
  105.  
  106. rval = []
  107. cell_hrule = ('+' + '-' * self.N) * self.N + '+\n'
  108. for block_row in range(0, self.N):
  109. rval.append(cell_hrule)
  110. for row in range(self.N * block_row, self.N * (block_row + 1)):
  111. for block_col in range(0, self.N):
  112. rval.append('|')
  113. for col in range(self.N * block_col, self.N * (block_col + 1)):
  114. cell = self.board[col][row]
  115. if cell == 0:
  116. rval.append(' ')
  117. elif cell < 10:
  118. rval.append(str(cell))
  119. else:
  120. rval.append(chr(ord('a') + cell - 10))
  121. rval.append('|\n')
  122.  
  123. rval.append(cell_hrule)
  124. return ''.join(rval)
  125.  
  126. def deb_render_conflicts(self):
  127. """Generate a Packages file that uses Conflicts as the main
  128. mechanism for describing the Sudoku board. This version is much more
  129. tractable for package managers than its Depends-based cousin."""
  130.  
  131. rval = []
  132.  
  133. # First, generate the constraints describing the Sudoku board.
  134.  
  135. for row in range(1, self.N * self.N + 1):
  136. block_row = (row - 1) / self.N + 1
  137. for col in range(1, self.N * self.N + 1):
  138. block_col = (col - 1) / self.N + 1
  139. for n in range(1, self.N * self.N + 1):
  140. boilerplate = pkg_boilerplate('cell%d.%d-%d' % (row, col, n),
  141. '1.0-1')
  142. rval.append('''%(boilerplate)s
  143. Provides: cell%(row)d.%(col)d, block%(block_row)d.%(block_col)d-%(n)d, row%(row)d-%(n)d, col%(col)d-%(n)d
  144. Conflicts: cell%(row)d.%(col)d, block%(block_row)d.%(block_col)d-%(n)d, row%(row)d-%(n)d, col%(col)d-%(n)d\n\n''' % {
  145. 'row' : row, 'col' : col, 'n' : n,
  146. 'block_row' : block_row, 'block_col' : block_col,
  147. 'boilerplate' : boilerplate } )
  148.  
  149. # Now add the package representing a solution.
  150. rval.append('%s\nDepends: ' % pkg_boilerplate('puzzle', '1.0-1'))
  151. cells = []
  152. for row, col, value in self.flatten():
  153. if value == 0:
  154. # Unconstrained, but require *something* in this cell.
  155. cells.append('cell%d.%d' % (row + 1, col + 1))
  156. else:
  157. cells.append('cell%d.%d-%d' % (row + 1, col + 1, value))
  158.  
  159. rval.append(', '.join(cells))
  160. rval.append('\n')
  161.  
  162. return ''.join(rval)
  163.  
  164. def deb_render_depends(self):
  165. """Generate a Packages file that uses Depends as the main
  166. mechanism for describing the Sudoku board. This version is much LESS
  167. tractable for package managers than its Conflicts-based cousin."""
  168. rval = []
  169.  
  170. # First, generate the constraints describing the Sudoku board.
  171.  
  172. # Each row must contain one instance of each number.
  173. for row in range(1, self.N * self.N + 1):
  174. boilerplate = pkg_boilerplate('row%d' % row, '1.0-1')
  175. rval.append('''%s
  176. Depends: %s\n\n''' % (boilerplate, ', '.join(['row%d-%d' % (row, n) for n in range(1, self.N * self.N + 1)])))
  177. # Each column must contain one instance of each number.
  178. for col in range(1, self.N * self.N + 1):
  179. boilerplate = pkg_boilerplate('col%d' % col, '1.0-1')
  180. rval.append('''%s
  181. Depends: %s\n\n''' % (boilerplate, ', '.join(['col%d-%d' % (col, n) for n in range(1, self.N * self.N + 1)])))
  182. # Each block must contain one instance of each number.
  183. for block_row in range(1, self.N + 1):
  184. for block_col in range(1, self.N + 1):
  185. boilerplate = pkg_boilerplate('block%d.%d' % (block_row, block_col),
  186. '1.0-1')
  187. rval.append('''%s
  188. Depends: %s\n\n''' % (boilerplate, ', '.join(['block%d.%d-%d' % (block_row, block_col, n) for n in range(1, self.N * self.N + 1)])))
  189.  
  190. # Each cell must only have a single value.
  191. for row in range(1, self.N * self.N + 1):
  192. block_row = (row - 1) / self.N + 1
  193. for col in range(1, self.N * self.N + 1):
  194. block_col = (col - 1) / self.N + 1
  195. for n in range(1, self.N * self.N + 1):
  196. boilerplate = pkg_boilerplate('cell%d.%d' % (row, col),
  197. '%d' % n)
  198. rval.append('''%(boilerplate)s
  199. Provides: block%(block_row)d.%(block_col)d-%(n)d, row%(row)d-%(n)d, col%(col)d-%(n)d\n\n''' % {
  200. 'row' : row, 'col' : col, 'n': n,
  201. 'block_row' : block_row, 'block_col' : block_col,
  202. 'boilerplate' : boilerplate } )
  203.  
  204. boilerplate = pkg_boilerplate('puzzle', '1.0')
  205.  
  206. # Generate dependencies that ensure that rows, columns, and
  207. # blocks are all correct.
  208. structure_depends = []
  209. for row in range(1, self.N * self.N + 1):
  210. structure_depends.append('row%d' % row)
  211. for col in range(1, self.N * self.N + 1):
  212. structure_depends.append('col%d' % col)
  213. for block_row in range(1, self.N + 1):
  214. for block_col in range(1, self.N + 1):
  215. structure_depends.append('block%d.%d' % (block_row, block_col))
  216.  
  217. # Generate dependencies corresponding to the values in the
  218. # input problem.
  219. problem_cell_depends = ['cell%d.%d (= %d)' % (row + 1, col + 1, value)
  220. for row, col, value in self.flatten() if value > 0]
  221. rval.append('''%s
  222. Depends: %s\n''' % (boilerplate, ', '.join(structure_depends + problem_cell_depends)))
  223.  
  224. return ''.join(rval)
  225.  
  226.  
  227.  
  228.  
  229. main()
Add Comment
Please, Sign In to add comment