fork download
  1. #!/usr/bin/python
  2. #coding: utf-8
  3.  
  4. import Tkinter as Tk
  5. import Dialog as D
  6. import ScrolledText as ST
  7. import random, time
  8. import sys
  9.  
  10. BLACK, WHITE, NONE = range(3)
  11.  
  12. def get_pos(i):
  13. return i % 8, i / 8
  14.  
  15. class Reversi:
  16. def __init__(self):
  17. self.xlist = (-1, 0, 1, -1, 1, -1, 0, 1)
  18. self.ylist = (-1, -1, -1, 0, 0, 1, 1, 1)
  19. self.init_game()
  20.  
  21. def init_game(self):
  22. global board
  23. board = [NONE for i in range(64)]
  24. board[8*3+3] = board[8*4+4] = WHITE
  25. board[8*4+3] = board[8*3+4] = BLACK
  26. global COUNT
  27. COUNT = 1
  28.  
  29. def put_stone(self, x, y, turn, count=True):
  30. global COUNT
  31. if board[8*y+x] != NONE:
  32. return False
  33. elif self.check_put(x, y, turn):
  34. if count: COUNT += 1
  35. self.put_stones(x, y, turn)
  36. return True
  37. else:
  38. return False
  39.  
  40. def check_put(self, x, y, turn):
  41. """ (x,y)の位置に石を置けるか判定する """
  42. revnum = 0
  43. for i in range(8):
  44. ax = self.xlist[i] + x
  45. ay = self.ylist[i] + y
  46. prevnum = 0
  47. while True:
  48. if not(0 <= ax <= 7 and 0 <= ay <= 7):
  49. break
  50. if board[8*ay+ax] == (not turn):
  51. prevnum += 1
  52. elif board[8*ay+ax] == turn and prevnum:
  53. revnum += prevnum
  54. break
  55. else:
  56. break
  57. ax += self.xlist[i]
  58. ay += self.ylist[i]
  59. return revnum
  60.  
  61. def put_stones(self, x, y, turn):
  62. """ (x, y)に石を置く """
  63. board[8*y+x] = turn
  64. for i in range(8):
  65. ax = self.xlist[i] + x
  66. ay = self.ylist[i] + y
  67. check_flag = put_flag = False
  68.  
  69. px = ax; py = ay
  70. while (0 <= px <= 7 and 0 <= py <= 7):
  71. if board[8*py+px] == (not turn):
  72. check_flag = True
  73. elif board[8*py+px] == turn and check_flag:
  74. put_flag = True
  75. else:
  76. break
  77. px += self.xlist[i]
  78. py += self.ylist[i]
  79.  
  80. while put_flag:
  81. if board[8*ay+ax] == (not turn):
  82. board[8*ay+ax] = turn
  83. else:
  84. break
  85. ax += self.xlist[i]
  86. ay += self.ylist[i]
  87.  
  88. def get_nummoves(self, turn):
  89. """ そのターンの手数をリストにする """
  90. nummoves = []
  91. for i in range(64):
  92. x, y = get_pos(i)
  93. if board[8*y+x] == NONE and self.check_put(x, y, turn):
  94. nummoves.append((x, y))
  95. return nummoves
  96.  
  97. def judge_remain(self, state):
  98. """ 白、黒が全滅、置く所がなくなったか判定する """
  99. return state not in board
  100.  
  101. def judge_end(self):
  102. """ どちらも置けなくなって終局になるか判定する """
  103. for stone in (BLACK, WHITE):
  104. if self.get_nummoves(stone):
  105. return False
  106. return True
  107.  
  108. class AI(Reversi):
  109. def __init__(self, stone):
  110. Reversi.__init__(self)
  111. self.stone = stone
  112. """
  113. weights = (120, -20, 20, 5, 5, 20, -20, 120, -20, -40, -5, -5,
  114. -5, -5, -40, -20, 20, -5, 15, 3, 3, 15, -5, 20, 5,
  115. -5, 3, 3, 3, 3, -5, 20)
  116. """
  117. weights = (30,-12,0,-1,-1,0,-12,30,-12,-15,-3,-3,-3,-3,-15,-12,
  118. 0,-3,0,-1,-1,0,-3,0,-1,-3,-1,-1,-1,-1,-3,-1)
  119.  
  120. self.wboard = [0 for i in range(64)]
  121. for i in range(len(weights)):
  122. self.wboard[i] = weights[i]
  123. self.wboard[63-i] = weights[i]
  124.  
  125. def put(self):
  126. self.nummoves = self.get_nummoves(self.stone)
  127. x, y = self.thinking()
  128. return x, y
  129.  
  130. def calc_weights(self):
  131. score = 0
  132. for i in range(64):
  133. if board[i] == self.stone:
  134. score += self.wboard[i]
  135. elif board[i] == (not self.stone):
  136. score -= self.wboard[i]
  137. return score
  138.  
  139. def judge_score(self):
  140. global board
  141. score_list = []
  142. for x, y in self.nummoves:
  143. backup = board[:]
  144. if self.put_stone(x, y, self.stone, count=False):
  145. score_list.append((self.calc_weights(), x, y))
  146. board = backup[:]
  147. x, y = max(score_list)[1:]
  148. return x, y
  149.  
  150. def thinking(self):
  151. # 鼠定石にならないようにする
  152. if self.stone == WHITE and COUNT == 2:
  153. nummoves = []
  154. for x, y in self.nummoves:
  155. count = 0
  156. for ax, ay in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
  157. if board[(y+ay)*8+(x+ax)] == (not self.stone):
  158. count += 1
  159. if count != 2:
  160. nummoves.append((x, y))
  161. else:
  162. random.choice(nummoves)
  163.  
  164. return self.judge_score()
  165.  
  166. if COUNT <= 15: #序盤
  167. pass
  168. elif COUNT <= 40: #中盤
  169. pass
  170. else: #終盤
  171. pass
  172.  
  173. class Board(Tk.Canvas):
  174. cs = 50
  175. def __init__(self, master, msg):
  176. Tk.Canvas.__init__(self, master, bg="white",
  177. width=self.cs*8, height=self.cs*8)
  178. self.msg = msg
  179. self.init()
  180. self.bind_all("<Button-1>", self.clicked)
  181. self.after(0, self.loop)
  182.  
  183. def init(self):
  184. self.cpp_id = None
  185. self.cpp = (-1, -1)
  186. self.ply_turn = random.randrange(2)
  187. if len(sys.argv) >= 2:
  188. self.ply_turn = int(sys.argv[1])
  189. self.com_turn = not self.ply_turn
  190. self.reversi = Reversi()
  191. self.ai = AI(self.com_turn)
  192. self.turn = BLACK
  193. self.draw_board()
  194. self.draw_stone()
  195. self.msg.update("プレイヤーの番です" if self.ply_turn == BLACK else "")
  196. self.player_time = time.time()
  197.  
  198. def game_set(self):
  199. self.end_game()
  200. for i in self.find_all(): self.delete(i)
  201. self.init()
  202.  
  203. def draw_board(self):
  204. self.create_rectangle(0, 0, self.cs*8, self.cs*8, fill="#008B00")
  205. for pos in range(1,8):
  206. pos *= self.cs
  207. self.create_line(pos, 0, pos, self.cs*8)
  208. self.create_line(0, pos, self.cs*8, pos)
  209. wid = 3
  210. for x, y in (2, 2), (2, 6), (6, 2), (6, 6):
  211. self.create_oval(self.cs*x-wid, self.cs*y-wid,
  212. self.cs*x+wid, self.cs*y+wid, fill="black")
  213.  
  214. def draw_stone(self):
  215. for y in range(8):
  216. for x in range(8):
  217. x0 = self.cs * x
  218. y0 = self.cs * y
  219. if board[8*y+x] == BLACK:
  220. self.create_oval(x0+3, y0+3, x0+self.cs-3,
  221. y0+self.cs-3, fill="#000000", width=1)
  222. elif board[8*y+x] == WHITE:
  223. self.create_oval(x0+3, y0+3, x0+self.cs-3,
  224. y0+self.cs-3, fill="#FFFFFF", width=1)
  225.  
  226. if self.cpp_id: self.delete(self.cpp_id)
  227. x, y = self.cpp
  228. x = self.cs * x + self.cs / 2
  229. y = self.cs * y + self.cs / 2
  230. self.cpp_id = self.create_oval(x-5, y-5, x+5, y+5, fill="red")
  231.  
  232. def loop(self):
  233. if self.reversi.judge_remain(NONE) or self.reversi.judge_end():
  234. self.game_set()
  235.  
  236. if (self.turn == self.com_turn and
  237. (time.time() - self.player_time) >= 0.3):
  238. self.turn = self.ply_turn
  239.  
  240. if not self.reversi.get_nummoves(self.com_turn):
  241. self.msg.update(text="COMはパスしました")
  242. else:
  243. x, y = self.ai.put()
  244. self.reversi.put_stone(x, y, self.com_turn)
  245. stone = "黒" if self.ply_turn==WHITE else "白"
  246. self.msg.update(text="プレイヤーの番です",
  247. pos="COM (%d,%d) %s"%(x, y, stone))
  248. self.cpp = (x, y)
  249. self.draw_stone()
  250. elif self.turn == self.ply_turn:
  251. if not self.reversi.get_nummoves(self.ply_turn):
  252. self.turn = self.com_turn
  253. self.after(50, self.loop)
  254.  
  255. def clicked(self, event):
  256. self.msg.update(text="")
  257. if self.turn != self.ply_turn: return
  258.  
  259. x = event.x / self.cs
  260. y = event.y / self.cs
  261.  
  262. result = self.reversi.put_stone(x, y, self.ply_turn)
  263. if result:
  264. self.draw_stone()
  265. self.turn = self.com_turn
  266. if self.reversi.judge_remain(self.turn): self.game_set()
  267. self.player_time = time.time()
  268. stone = "黒" if self.ply_turn == BLACK else "白"
  269. self.msg.update(pos="PLAYER (%d,%d) %s" %(x, y, stone))
  270. else:
  271. self.msg.update(text="そこには置けません")
  272.  
  273. def end_game(self):
  274. bn = wn = 0
  275. ply = com = 0
  276. for i in range(64):
  277. x, y = get_pos(i)
  278. if board[8*y+x] == BLACK:
  279. bn += 1
  280. elif board[8*y+x] == WHITE:
  281. wn += 1
  282. if board[8*y+x] == self.ply_turn:
  283. ply += 1
  284. elif board[8*y+x] == self.com_turn:
  285. com += 1
  286.  
  287. if ply > com:
  288. text = "プレイヤーの勝ち"
  289. elif ply < com:
  290. text = "COMの勝ち"
  291. else:
  292. text = "引き分け"
  293. text = "黒:%d 白:%d " % (bn,wn) + "\n" + text
  294. dialog = D.Dialog(title="勝敗", text=text, bitmap="question",
  295. default=0, strings=["OK"])
  296. self.msg.clear()
  297. self.turn = BLACK
  298.  
  299. class Message:
  300. def __init__(self, master):
  301. self.msg = Tk.Label(master, relief=Tk.RIDGE, bd=2)
  302. self.msg.pack(fill=Tk.X)
  303. self.log = ST.ScrolledText(master, state=Tk.DISABLED,
  304. font=("Monospace", "12"),
  305. height=5, width=1)
  306. self.log.pack(side=Tk.BOTTOM, fill=Tk.X)
  307.  
  308. def update(self, text=None, pos=None):
  309. if text is not None:
  310. self.msg.config(text=text)
  311. if pos is not None:
  312. self.log.config(state=Tk.NORMAL)
  313. self.log.insert(Tk.END, pos+"\n")
  314. self.log.config(state=Tk.DISABLED)
  315. self.log.see(Tk.END)
  316.  
  317. def clear(self):
  318. self.msg.config(text="")
  319. self.log.config(state=Tk.NORMAL)
  320. self.log.delete("1.0", Tk.END)
  321. self.log.config(state=Tk.DISABLED)
  322.  
  323. class Application(Tk.Frame):
  324. def __init__(self, master=None):
  325. Tk.Frame.__init__(self, master)
  326. frame = Tk.Frame()
  327. message = Message(frame)
  328. self.board = Board(frame, message)
  329. self.board.pack(side=Tk.TOP)
  330. frame.pack()
  331. self.set_menubar()
  332.  
  333. def set_menubar(self):
  334. menubar = Tk.Menu(self, tearoff=0)
  335. filemenu = Tk.Menu(menubar, tearoff=0)
  336. menubar.add_cascade(label=u"ゲーム(G)", menu=filemenu, underline=5)
  337. filemenu.add_command(label=u"最初から", command=self.board.init)
  338. self.master.config(menu=menubar)
  339.  
  340. if __name__ == "__main__":
  341. frame = Application()
  342. frame.pack()
  343. frame.mainloop()
  344.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty