Advertisement
Guest User

Untitled

a guest
Apr 21st, 2015
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.35 KB | None | 0 0
  1. #!/usr/bin/python2
  2. from minesweeper import *
  3. import itertools
  4. import random
  5.  
  6.  
  7. # Patterns syntax:
  8. # Each pattern is an array of string
  9. # Each string contains pairs <matcher, action> characters (e.g. ?o) separated by whitespace for readability
  10. # match - is a matcher against which element on board will be matched. All elements in patterns must match for action to occur
  11. # possible matchers are:
  12. # ? - match anything, including out of bonds cells
  13. # 0..9 - match against reduced strength
  14. # Reduced strength is (written number in field) - (number of marked mines adjacent to the cell)
  15. # doesn't match out of bonds cells
  16. # K - known cell, impossible for the new mine cell. Matches out-of-bounds, known 0-9, known mines, not unknowns
  17. # . - matches unknown
  18. # A - matches empty cell when TOTAL_MINES_LEFT = 1
  19. # action is an action regarding this cell
  20. # o - open it. Action is ignored if cell already marked as mine or open or out of bounds
  21. # * - mark mine
  22. # _ - ignore cell.
  23.  
  24.  
  25.  
  26. PATTERNS = [
  27. ["?o ?o ?o",
  28. "?o 0_ ?o",
  29. "?o ?o ?o"],
  30.  
  31. [ "K_ K_ K_",
  32. "1_ 2_ K_",
  33. "?_ ?_ .*"],
  34.  
  35. [ "K_ K_ K_ K_",
  36. "K_ 1_ 1_ K_",
  37. "?o ?_ ?_ K_"],
  38.  
  39. ["K_ 1_ 1_ K_",
  40. "K_ ._ ._ K_",
  41. "K_ 1_ .o K_",
  42. "K_ K_ K_ K_"],
  43.  
  44. ]
  45.  
  46. A_PATTERNS = [
  47. ["K_ 1_",
  48. "1_ A*"],
  49.  
  50. ["1_ ?_",
  51. "K_ A*",
  52. "K_ 1_"],
  53.  
  54. ["1_ ?_",
  55. "K_ A*",
  56. "1_ ?_"],
  57. ]
  58.  
  59.  
  60. TRANSPOSED_PATTERNS = {}
  61. HFLIPPED_PATTERNS = {}
  62.  
  63. def transpose_pattern(p):
  64. if id(p) in TRANSPOSED_PATTERNS:
  65. return TRANSPOSED_PATTERNS[id(p)]
  66.  
  67. res = []
  68. splitted = [x.split() for x in p]
  69. z = zip(*splitted)
  70. z = [" ".join(x) for x in z]
  71. TRANSPOSED_PATTERNS[id(p)] = z
  72. return z
  73.  
  74. def hflip_pattern(p):
  75. if id(p) in HFLIPPED_PATTERNS:
  76. return HFLIPPED_PATTERNS[id(p)]
  77.  
  78. res = []
  79. splitted = [reversed(x.split()) for x in p]
  80. z = [" ".join(x) for x in splitted]
  81. HFLIPPED_PATTERNS[id(p)] = z
  82. return z
  83.  
  84. def calculate_pattern_offsets(pattern):
  85. """ Calculate pattern offset. Since ? matcher can match against empty cell, we need to know how far out of bonds we can go
  86. :return: (row_from, row_to, col_from, col_to)
  87. """
  88. row_from = 0
  89. row_to = 0
  90. col_from = 0
  91. col_to = 0
  92.  
  93. pattern_height = len(pattern)
  94. patter_width = len(pattern[0])
  95.  
  96. def impl(p):
  97. xfrom = xto = 0
  98. # First try to find how far we can move pattern out of top bounds
  99. for row in p:
  100. if any(map(lambda p:p[0] not in '?K', row.split())):
  101. break
  102. xfrom -= 1
  103.  
  104. # Now bottom
  105. for row in reversed(p):
  106. if any(map(lambda p:p[0] not in '?K', row.split())):
  107. break
  108. xto += 1
  109. return xfrom, xto
  110.  
  111. row_from, row_to = impl(pattern)
  112. col_from, col_to = impl(transpose_pattern(pattern))
  113.  
  114. return (row_from, row_to, col_from, col_to)
  115.  
  116. PATTERN_OFFSETS = { id(p) : calculate_pattern_offsets(p) for p in PATTERNS }
  117. for pa in A_PATTERNS:
  118. PATTERN_OFFSETS[id(pa)] = calculate_pattern_offsets(pa)
  119.  
  120.  
  121.  
  122.  
  123. def print_grid(grid):
  124. for row in range(grid.nrows):
  125. s = ""
  126. for col in range(grid.ncols):
  127. s += "%2s " % grid[row,col]
  128. print(s)
  129.  
  130. print("Mines left: %d" % TOTAL_MINES_LEFT)
  131.  
  132.  
  133.  
  134. def check_match_at(grid, reduced, pattern, row, col):
  135. cols = grid.nCols()
  136. rows = grid.nRows()
  137. y = row
  138. res = set()
  139.  
  140. for prow in pattern:
  141. x = col - 1
  142. for pair in prow:
  143. x += 1
  144. matcher = pair[0]
  145. action = pair[1]
  146. in_bounds = 0 <= y < rows and 0 <= x < cols
  147. if in_bounds and reduced[y,x] == '.' and action != '_':
  148. res.add((y,x, action == 'o'))
  149.  
  150. if matcher == 'A':
  151. if TOTAL_MINES_LEFT != 1:
  152. return None
  153. if not in_bounds or reduced[y, x] != '.':
  154. return None
  155. continue
  156.  
  157. if matcher == '.':
  158. if not in_bounds or reduced[y, x] != '.':
  159. return None
  160. continue
  161.  
  162. if matcher == '?':
  163. continue
  164.  
  165. if matcher == 'K':
  166. if in_bounds and reduced[y, x] in ['.']:
  167. return None
  168. continue
  169.  
  170. if not in_bounds:
  171. return None
  172.  
  173. if int(matcher) != reduced[y,x]:
  174. return None
  175. y += 1
  176. return res
  177.  
  178.  
  179.  
  180. def do_check_match(grid, reduced, pattern, pattern_offset):
  181. pattern_height = len(pattern)
  182. patter_width = len(pattern[0])
  183. row_from, row_to, col_from, col_to = pattern_offset
  184. col_to = grid.nCols() - col_to
  185. row_to = grid.nRows() - row_to
  186. pairs = [p.split() for p in pattern]
  187. for row in range(row_from, row_to):
  188. for col in range(col_from, col_to):
  189. res = check_match_at(grid, reduced, pairs, row, col)
  190. if res:
  191. return res
  192. return None
  193.  
  194. def set_add(the_set, elt):
  195. if elt in the_set:
  196. return False
  197. the_set.add(elt)
  198. return True
  199.  
  200. def check_match(grid, reduced, pattern):
  201. processed = set()
  202.  
  203. def check_flips(p, ofs):
  204. # Vertically flipped pattern
  205. # E R N _
  206. # P A T T
  207. vpattern = list(reversed(p))
  208. voffset = (-ofs[1], -ofs[0], ofs[2], ofs[3])
  209. if set_add(processed, "_".join(vpattern)):
  210. res = do_check_match(grid, reduced, vpattern, voffset)
  211. if res:
  212. return res
  213.  
  214. # Horizontally flipped pattern
  215. # T T A P
  216. # _ N R E
  217. hoffset = (ofs[0], ofs[1], -ofs[3], -ofs[2])
  218. hpattern = hflip_pattern(p)
  219. if set_add(processed, "_".join(hpattern)):
  220. res = do_check_match(grid, reduced, hpattern, hoffset)
  221. if res:
  222. return res
  223.  
  224. # Flipped both way
  225. # _ N R E
  226. # T T A P
  227. vhoffset = (-ofs[1], -ofs[0], -ofs[3], -ofs[2])
  228. vhpattern = hflip_pattern(vpattern)
  229. if set_add(processed, "_".join(vhpattern)):
  230. res = do_check_match(grid, reduced, vhpattern, vhoffset)
  231. return res
  232. return None
  233.  
  234. # normal pattern
  235. # P A T T
  236. # E R N _
  237. offset = PATTERN_OFFSETS[id(pattern)]
  238. res = do_check_match(grid, reduced, pattern, offset)
  239. if res:
  240. return res
  241.  
  242. res = check_flips(pattern, offset)
  243. if res:
  244. return res
  245.  
  246.  
  247.  
  248. # transposes pattern
  249. # P E
  250. # A R
  251. # T N
  252. # T _
  253.  
  254. tpattern = transpose_pattern(pattern)
  255.  
  256. toffset = (offset[2], offset[3], offset[0], offset[1])
  257. res = do_check_match(grid, reduced, tpattern, toffset)
  258. if res:
  259. return res
  260.  
  261. res = check_flips(tpattern, toffset)
  262. return res
  263.  
  264. def iter_neigbours(arr2d, row, col):
  265. for dx in (-1,0,1):
  266. for dy in (-1,0,1):
  267. if dx == dy == 0:
  268. continue
  269. cx, cy = col + dx, row + dy
  270. if 0 <= cx < arr2d.ncols and 0 <= cy < arr2d.nrows:
  271. yield cy, cx
  272.  
  273. def mark_mine_at(reduced, y, x):
  274. global TOTAL_MINES_LEFT
  275.  
  276. if reduced[y, x] == '*':
  277. return False
  278.  
  279. reduced[y, x] = '*'
  280. TOTAL_MINES_LEFT -= 1
  281. for row, col in iter_neigbours(reduced, y, x):
  282. r = reduced[row, col]
  283. if type(r) == int and r > 0:
  284. r -= 1
  285. reduced[row, col] = r
  286. return True
  287.  
  288. def try_to_fill(grid, reduced):
  289. res = False
  290. for y in range(reduced.nrows):
  291. for x in range(reduced.ncols):
  292. if not type(reduced[y,x]) == int:
  293. continue
  294. if reduced[y,x] == 0:
  295. continue
  296.  
  297.  
  298. total_neighbours = 0
  299. unknowns = 0
  300.  
  301. for cy, cx in iter_neigbours(reduced, y, x):
  302. total_neighbours += 1
  303. if reduced[cy,cx] == ".":
  304. unknowns += 1
  305.  
  306. if reduced[y,x] == unknowns:
  307. for cy, cx in iter_neigbours(reduced, y, x):
  308. if reduced[cy,cx] == ".":
  309. res = mark_mine_at(reduced, cy, cx) or res
  310.  
  311. if TOTAL_MINES_LEFT == 0:
  312. res = set([])
  313. for y in range(reduced.nrows):
  314. for x in range(reduced.ncols):
  315. if reduced[y,x] == '.':
  316. do_guess(grid, reduced, y, x)
  317. res.add((y, x, True))
  318.  
  319.  
  320. return res
  321.  
  322.  
  323. TOTAL_MINES_LEFT = 0
  324.  
  325. STEP = 0
  326.  
  327. def do_guess(grid, reduced, row, col):
  328. grid.guess(row, col)
  329. m = grid.nMinesAround(row, col)
  330. for cy, cx in iter_neigbours(reduced, row, col):
  331. if reduced[cy, cx] == '*':
  332. m -= 1
  333. reduced[row,col] = m
  334.  
  335. def exampleSolveGame(grid):
  336. global STEP
  337. global TOTAL_MINES_LEFT
  338. STEP += 1
  339. TOTAL_MINES_LEFT = grid.nMines()
  340. reduced = Array2D(grid.nRows(), grid.nCols(), ".");
  341. do_guess(grid, reduced, 4, 4)
  342. while not grid.isSolved():
  343. old=True
  344.  
  345. l = PATTERNS
  346. if TOTAL_MINES_LEFT == 1:
  347. l += A_PATTERNS
  348.  
  349. for p in l:
  350. res = check_match(grid, reduced, p)
  351. if res:
  352. for row, col, is_empty in res:
  353. if is_empty:
  354. do_guess(grid, reduced, row, col)
  355. else:
  356. mark_mine_at(reduced, row, col)
  357. old = False
  358. break
  359. if not old:
  360. continue
  361.  
  362. res = try_to_fill(grid, reduced)
  363.  
  364. if res:
  365. continue
  366.  
  367. while True:
  368. cy = random.randint(0, reduced.nrows-1)
  369. cx = random.randint(0, reduced.ncols-1)
  370. if reduced[cy,cx] == '.':
  371. do_guess(grid, reduced, cy, cx)
  372. break
  373.  
  374.  
  375. def main():
  376. # Set this to True if you want to play a game of Minesweeper with a
  377. # crappy text interface. Set this to False if you want to test
  378. # how good the example AI is (spoiler: not very).
  379. doYouWantToPlayAGame = False
  380.  
  381. if doYouWantToPlayAGame:
  382. playGame(9, 9, 10)
  383. else:
  384. testSolver(exampleSolveGame)
  385.  
  386. if __name__ == "__main__":
  387. random.seed(12475)
  388. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement