Advertisement
BoogeyCZ

Untitled

Apr 23rd, 2013
346
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.29 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # If you would like to run it on merlin, please use python2.4 instead.
  5. #
  6. # Author: Izidor Matušov <izidor.matusov@gmail.com>
  7. # Date: 09.02.2013
  8.  
  9. import os
  10. import re
  11. import sys
  12. from math import pi
  13. from subprocess import Popen, PIPE
  14. from time import time
  15.  
  16. try:
  17. import gtk
  18. except ImportError:
  19. print("PyGTK is available to python2.4 on Merlin, run it like this:\n")
  20. print("python2.4 %s" % (" ".join(sys.argv)))
  21. sys.exit(0)
  22. import gobject
  23.  
  24. BOARD_SIZE = 19
  25. CONNECT_N = 6
  26.  
  27. class Canvas(gtk.DrawingArea):
  28. """ Canvas board for Connect-k """
  29.  
  30. __gsignals__ = {
  31. 'quit' : (gobject.SIGNAL_RUN_FIRST, None, [bool]),
  32. 'first' : (gobject.SIGNAL_RUN_FIRST, None, [int, int]),
  33. 'stones' : (gobject.SIGNAL_RUN_FIRST, None, [int, int, int, int]),
  34. }
  35.  
  36. def __init__(self, is_first):
  37. super(Canvas, self).__init__()
  38. self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
  39. self.connect('expose-event', self.on_draw)
  40. self._click_handler = self.connect('button-press-event', self.on_click)
  41.  
  42. # Board
  43. self.board = [[None for i in range(BOARD_SIZE)] for i in range(BOARD_SIZE)]
  44. # Which things to highlight?
  45. self.solution = None
  46. # Moves to send
  47. self.stones = []
  48.  
  49. # Was the first move already played?
  50. self.first_move = is_first
  51. # Player's & enemy's symbol
  52. if is_first:
  53. self.player, self.enemy = 0, 1
  54. else:
  55. self.player, self.enemy = 1, 0
  56.  
  57. def on_draw(self, drawing_area, event):
  58. """ Draw the canvas:
  59.  
  60. * background -- white space
  61. * highlighted solution
  62. * grid
  63. * stones
  64. """
  65. alloc = self.get_allocation()
  66. width, height = alloc[2], alloc[3]
  67. size_x, size_y = width // BOARD_SIZE, height // BOARD_SIZE
  68.  
  69. cr_ctxt = self.window.cairo_create() # pylint: disable-msg=E1101
  70. cairo = gtk.gdk.CairoContext(cr_ctxt)
  71.  
  72. # Background
  73. cairo.set_source_rgb(0.7, 0.7, 0.7)
  74. cairo.rectangle(0, 0, width, height)
  75. cairo.fill()
  76.  
  77. # Highlight the win
  78. if self.solution is not None:
  79. x, y, t = self.solution
  80. player = self.board[y][x]
  81. cairo.set_source_rgb(1, 0.8, 1)
  82. while 0 <= y < BOARD_SIZE and 0 <= x < BOARD_SIZE and self.board[y][x] == player:
  83. cairo.rectangle(x * size_x, y * size_y,
  84. size_x, size_y)
  85.  
  86. if t == 'h':
  87. x, y = x + 1, y
  88. elif t == 'v':
  89. x, y = x, y + 1
  90. elif t == 'd1':
  91. x, y = x + 1, y + 1
  92. elif t == 'd2':
  93. x, y = x - 1, y + 1
  94.  
  95. cairo.fill()
  96.  
  97. # Grid
  98. cairo.set_source_rgb(0, 0, 0)
  99. cairo.set_line_width(1)
  100.  
  101. # Horizontal
  102. for i in range(BOARD_SIZE):
  103. y = i * size_y
  104. cairo.move_to(0, y)
  105. cairo.line_to(width, y)
  106. # Vertical
  107. for i in range(BOARD_SIZE):
  108. x = i * size_x
  109. cairo.move_to(x, 0)
  110. cairo.line_to(x, height)
  111. cairo.stroke()
  112.  
  113. # Points
  114. for y in range(BOARD_SIZE):
  115. for x in range(BOARD_SIZE):
  116. owner = self.board[y][x]
  117. base_x, base_y = x * size_x, y * size_y
  118.  
  119. if owner is None:
  120. continue
  121.  
  122. if owner == 0:
  123. cairo.set_source_rgb(0, 0, 0)
  124. else:
  125. cairo.set_source_rgb(1, 1, 1)
  126.  
  127. cairo.set_line_width(2)
  128. radius = min(size_x, size_y) // 2 - 5
  129. cairo.arc(base_x + size_x // 2, base_y + size_y // 2, radius, 0, 2 * pi)
  130. cairo.fill()
  131.  
  132. return False
  133.  
  134. def on_click(self, widget, event, data=None):
  135. """ Handle click event """
  136. alloc = self.get_allocation()
  137. width, height = alloc[2], alloc[3]
  138. size_x, size_y = width // BOARD_SIZE, height // BOARD_SIZE
  139.  
  140. # if the last row/column is not a square, handle it correctly
  141. x = min(int(event.x // size_x), BOARD_SIZE - 1)
  142. y = min(int(event.y // size_y), BOARD_SIZE - 1)
  143.  
  144. if self.board[y][x] is None:
  145. self.board[y][x] = self.player
  146. # Protocol uses 0-based coords
  147. self.stones.append(x + 1)
  148. self.stones.append(y + 1)
  149.  
  150. # Check solution only after complete move
  151. if len(self.stones) >= 4:
  152. self.solution = self.check_board()
  153. self.queue_draw()
  154.  
  155. if self.first_move and len(self.stones) >= 2:
  156. self.first_move = False
  157. self.emit('first', *self.stones)
  158. self.stones = []
  159. elif len(self.stones) >= 4:
  160. # Don't allow further input when one of players won
  161. if self.solution is not None:
  162. self.disconnect(self._click_handler)
  163. x, y, _ = self.solution
  164. assert self.board[y][x] is not None
  165. self.emit('quit', self.player == self.board[y][x])
  166. return True
  167.  
  168. self.emit('stones', *self.stones)
  169. self.stones = []
  170. return True
  171.  
  172. def enemy_move(self, moves):
  173. """ Mark moves of the enemy """
  174. for x, y in moves:
  175. assert 1 <= x <= BOARD_SIZE and 1 <= y <= BOARD_SIZE
  176. # Convert to 0-based coords
  177. x, y = x - 1, y - 1
  178. assert self.board[y][x] is None
  179. self.board[y][x] = self.enemy
  180. self.solution = self.check_board()
  181. if self.solution is not None:
  182. self.disconnect(self._click_handler)
  183. x, y, _ = self.solution
  184. assert self.board[y][x] is not None
  185. self.emit('quit', self.player == self.board[y][x])
  186. self.queue_draw()
  187.  
  188. def check_board(self):
  189. """ Check board if somebody wins
  190.  
  191. Return (x, y, type) where type can be:
  192. * h -- horizontal
  193. * v -- vertical
  194. * d1 -- left diagonal
  195. * d2 -- right diagonal
  196. """
  197. for y in range(BOARD_SIZE):
  198. for x in range(BOARD_SIZE):
  199. player = self.board[y][x]
  200. if player is None:
  201. continue
  202.  
  203. for i in range(1, CONNECT_N):
  204. dx = x + i
  205. if dx >= BOARD_SIZE:
  206. break
  207. if self.board[y][dx] != player:
  208. break
  209. else:
  210. return (x, y, 'h')
  211.  
  212. for i in range(1, CONNECT_N):
  213. dy = y + i
  214. if dy >= BOARD_SIZE:
  215. break
  216. if self.board[dy][x] != player:
  217. break
  218. else:
  219. return (x, y, 'v')
  220.  
  221. for i in range(1, CONNECT_N):
  222. dx, dy = x + i, y + i
  223. if dx >= BOARD_SIZE or dy >= BOARD_SIZE:
  224. break
  225. if self.board[dy][dx] != player:
  226. break
  227. else:
  228. return (x, y, 'd1')
  229.  
  230. for i in range(1, CONNECT_N):
  231. dx, dy = x - i, y + i
  232. if 0 > dx or dy >= BOARD_SIZE:
  233. break
  234. if self.board[dy][dx] != player:
  235. break
  236. else:
  237. return (x, y, 'd2')
  238.  
  239.  
  240. class Prolog(object):
  241. """ Object for communication with prolog code """
  242.  
  243. def __init__(self):
  244. self.proc = Popen([self._find_binary()], bufsize=1, stdin=PIPE, stdout=PIPE)
  245. self.last_duration = None
  246.  
  247. def _find_binary(self):
  248. """ Run make and return executable in format of xlogin00 """
  249. os.system("make")
  250. for fpath in os.listdir('.'):
  251. if os.access(fpath, os.X_OK) and \
  252. re.match(r'x[a-z]{5}[0-9]{2}', fpath):
  253. return "./" + fpath
  254.  
  255. def _cmd(self, cmd):
  256. """ Pass a command to prolog binary
  257.  
  258. Track the time needed to respond.
  259. """
  260. print("<-- " + cmd[:-1])
  261. start = time()
  262. self.proc.stdin.write(cmd.encode())
  263. self.proc.stdin.flush()
  264. out = self.proc.stdout.readline()
  265. self.last_duration = time() - start
  266. response = out.decode()
  267. print("--> " + response[:-1])
  268. print("")
  269. return response
  270.  
  271. def start(self):
  272. """ Send START command """
  273. move = self._cmd("START;\n")
  274. x, y = move.strip(';\n').split(':')[1].split(',')
  275. return (int(x), int(y))
  276.  
  277. def _parse_stones(self, response):
  278. """ Parse response STONES:x1,y1;x2,y2; """
  279. raw_moves = response.strip(';\n').split(':')[1].split(';')
  280. moves = [(int(x) for x in move.split(',')) for move in raw_moves]
  281. assert len(moves) == 2
  282. return moves
  283.  
  284. def first(self, x, y):
  285. """ Send FIRST command """
  286. cmd = "FIRST:%d,%d;\n" % (x, y)
  287. return self._parse_stones(self._cmd(cmd))
  288.  
  289. def stones(self, x1, y1, x2, y2):
  290. """ Send STONES command """
  291. cmd = "STONES:%d,%d;%d,%d;\n" % (x1, y1, x2, y2)
  292. return self._parse_stones(self._cmd(cmd))
  293.  
  294. def quit(self):
  295. """ Send QUIT command """
  296. self._cmd("QUIT;\n")
  297. self.proc.communicate()
  298.  
  299. def enter(self):
  300. self._cmd("\n")
  301. self.proc.communicate()
  302.  
  303.  
  304. class App(object):
  305. """ Class for connecting Canvas, gtk and prolog """
  306.  
  307. def __init__(self, is_first):
  308. window = gtk.Window()
  309. window.set_title('FLP :: Connect6')
  310. window.set_resizable(False)
  311. window.connect('destroy', gtk.main_quit)
  312.  
  313. main_vbox = gtk.VBox()
  314.  
  315. self.info = gtk.Label()
  316. self.info.set_alignment(0, 0.5)
  317. main_vbox.pack_start(self.info, False, False, 10)
  318.  
  319. self.button = gtk.Button("Enter")
  320. self.button.connect('clicked', self.click)
  321. main_vbox.pack_start(self.button, False, False, 10)
  322.  
  323. self.canvas = Canvas(is_first)
  324. self.canvas.set_size_request(608, 608)
  325. self.canvas.connect('first', self.on_first)
  326. self.canvas.connect('stones', self.on_stones)
  327. self.canvas.connect('quit', self.on_quit)
  328. main_vbox.pack_start(self.canvas, True, True, 0)
  329.  
  330. window.add(main_vbox)
  331. window.show_all()
  332.  
  333. self.prolog = Prolog()
  334. self.is_first = is_first
  335. self.winner = None
  336. if self.is_first:
  337. self.update_info()
  338. else:
  339. self.on_start()
  340.  
  341. def update_info(self):
  342. """ Update markup of the info bar """
  343. if self.winner is None:
  344. if self.is_first:
  345. starter = "You have"
  346. else:
  347. starter = "Prolog has"
  348.  
  349. text = "%s black stones " % (starter)
  350.  
  351. if self.prolog.last_duration is None:
  352. response = 0
  353. else:
  354. response = self.prolog.last_duration
  355.  
  356. if response >= 1:
  357. markup_start, markup_end = "<span foreground='red'>", "</span>"
  358. else:
  359. markup_start, markup_end = "",""
  360.  
  361. text += "Response time: %s%.3fs%s" % (markup_start,
  362. response, markup_end)
  363. self.info.set_markup(text)
  364. elif self.winner:
  365. self.info.set_markup("<b>You have won!</b>")
  366. self.info.set_alignment(0.5, 0.5)
  367. else:
  368. self.info.set_markup("<b>Prolog has won!</b>")
  369. self.info.set_alignment(0.5, 0.5)
  370.  
  371. def on_start(self):
  372. """ Let prolog start """
  373. move = self.prolog.start()
  374. self.canvas.enemy_move([move])
  375. self.update_info()
  376.  
  377. def on_first(self, widget, x, y):
  378. """ The first stone was placed """
  379. moves = self.prolog.first(x, y)
  380. self.canvas.enemy_move(moves)
  381. self.update_info()
  382.  
  383. def on_stones(self, widget, x1, y1, x2, y2):
  384. """ Two stones were placed """
  385. moves = self.prolog.stones(x1, y1, x2, y2)
  386. self.canvas.enemy_move(moves)
  387. self.update_info()
  388.  
  389. def on_quit(self, widget, is_winner):
  390. """ Finish prolog """
  391. self.winner = is_winner
  392. self.prolog.quit()
  393. self.update_info()
  394.  
  395. def click(self, n):
  396. self.prolog.enter()
  397.  
  398.  
  399. if __name__ == "__main__":
  400. is_first = False
  401. if len(sys.argv) >= 2:
  402. if "--help" in sys.argv[1:]:
  403. print("""Human interface for Connect6
  404. FLP, Logic project
  405. Created by Izidor Matusov <xmatus19@stud.fit.vutbr.cz>
  406.  
  407. {without arguments}\tyou are the second (white) player
  408. <me|first|start|begin>\tstart as the first (black) player
  409. --help\tprint this help message""")
  410. sys.exit(0)
  411.  
  412. for expr in ["me", "first", "start", "begin"]:
  413. if expr in sys.argv[1:]:
  414. is_first = True
  415. break
  416.  
  417. App(is_first)
  418. gtk.main()
  419.  
  420. # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement