snake.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-"
  3. """
  4. PyAISnake - 2018/2020 - by psy (epsylon@riseup.net)
  5. You should have received a copy of the GNU General Public License along
  6. with PyAISnake; if not, write to the Free Software Foundation, Inc., 51
  7. Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  8. """
  9. import curses, random, time
  10. from curses import KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN
  11. ######################################################################3
  12. # import AI models into sandbox
  13. from models.AIBob import AIBob
  14. from models.AIAlice import AIAlice
  15. bots = ['AIBoB','AIAlice']
  16. ######################################################################3
  17. class BoxSelector:
  18. def __init__(self, bots):
  19. self.TEXTBOX_WIDTH = 50
  20. self.TEXTBOX_HEIGHT = 6
  21. self.PAD_WIDTH = 400
  22. self.PAD_HEIGHT = 10000
  23. def pick(self):
  24. self._init_curses()
  25. self._create_pad()
  26. windows = self._make_textboxes()
  27. picked = self._select_textbox(windows)
  28. self._end_curses()
  29. return picked
  30. def _init_curses(self):
  31. self.stdscr = curses.initscr()
  32. curses.noecho()
  33. curses.cbreak()
  34. curses.curs_set(0)
  35. self.stdscr.keypad(1)
  36. curses.start_color()
  37. curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_GREEN)
  38. curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK)
  39. self.stdscr.bkgd(curses.color_pair(2))
  40. self.stdscr.refresh()
  41. def _end_curses(self):
  42. curses.nocbreak()
  43. self.stdscr.keypad(0)
  44. curses.echo()
  45. curses.endwin()
  46. def _create_pad(self):
  47. self.pad = curses.newpad(self.PAD_HEIGHT, self.PAD_WIDTH)
  48. self.pad.box()
  49. def _make_textboxes(self):
  50. maxy, maxx = self.stdscr.getmaxyx()
  51. windows = []
  52. i = 1
  53. for s in bots:
  54. windows.append(self.pad.derwin(self.TEXTBOX_HEIGHT,
  55. self.TEXTBOX_WIDTH, i, self.PAD_WIDTH//2-self.TEXTBOX_WIDTH//2))
  56. i += self.TEXTBOX_HEIGHT
  57. for k in range(len(windows)):
  58. windows[k].box()
  59. windows[k].addstr(4, 4, '{0:X} - {1}'.format(k, bots[k]))
  60. return windows
  61. def _center_view(self, window):
  62. cy, cx = window.getbegyx()
  63. maxy, maxx = self.stdscr.getmaxyx()
  64. self.pad.refresh(cy, cx, 1, maxx//2 - self.TEXTBOX_WIDTH//2, maxy-1, maxx-1)
  65. return (cy, cx)
  66. def _select_textbox(self, windows):
  67. topy, topx = self._center_view(windows[0])
  68. current_selected = 0
  69. last = 1
  70. top_textbox = windows[0]
  71. while True:
  72. windows[current_selected].bkgd(curses.color_pair(1))
  73. windows[last].bkgd(curses.color_pair(2))
  74. maxy, maxx = self.stdscr.getmaxyx()
  75. cy, cx = windows[current_selected].getbegyx()
  76. if ((topy + maxy - self.TEXTBOX_HEIGHT) <= cy):
  77. top_textbox = windows[current_selected]
  78. if topy >= cy + self.TEXTBOX_HEIGHT:
  79. top_textbox = windows[current_selected]
  80. if last != current_selected:
  81. last = current_selected
  82. topy, topx = self._center_view(top_textbox)
  83. c = self.stdscr.getch()
  84. if c == ord('0') or c == 258: # KEY_UP
  85. if current_selected >= len(windows)-1:
  86. current_selected = 0
  87. else:
  88. current_selected += 1
  89. elif c == ord('1') or c == 259: # KEY_DOWN
  90. if current_selected <= 0:
  91. current_selected = len(windows)-1
  92. else:
  93. current_selected -= 1
  94. elif c == ord('q') or c == 27: # ESC
  95. break
  96. elif c == curses.KEY_ENTER or c == 10:
  97. return int(current_selected)
  98. # translate a thought into a text
  99. def text_to_thought(move):
  100. if move == KEY_UP:
  101. thought = "UP"
  102. elif move == KEY_LEFT:
  103. thought = "LEFT"
  104. elif move == KEY_RIGHT:
  105. thought = "RIGHT"
  106. elif move == KEY_DOWN:
  107. thought = "DOWN"
  108. else:
  109. thought = "LEARNING..."
  110. return thought
  111. # create matrix (with some random init values)
  112. def init(evol, record, max_moves):
  113. curses.initscr()
  114. x_height = 0
  115. y_height = 20
  116. x_width = 60
  117. y_width = 0
  118. win = curses.newwin(y_height, x_width, x_height, y_width)
  119. win.keypad(1)
  120. curses.noecho()
  121. curses.curs_set(0)
  122. win.border(0)
  123. win.nodelay(1)
  124. food = [random.randint(1, 18), random.randint(1, 58)] # set resources into matrix
  125. win.addch(food[0], food[1], '*')
  126. p1_a1 = random.randint(1, 10)
  127. p1_a2 = random.randint(1, 10)
  128. p1_b1 = random.randint(1, 10)
  129. p1_b2 = random.randint(1, 10)
  130. p1_c1 = random.randint(1, 10)
  131. p1_c2 = random.randint(1, 10)
  132. snake = [[p1_a1,p1_a2], [p1_b1,p1_b2], [p1_c1,p1_c2]] # generate starting point
  133. startGame(win, food, snake, evol, record, max_moves) # start NEW GAME!
  134. # start a new game simulation
  135. def startGame(win, food, snake, evol, record, max_moves):
  136. moves = 0
  137. score = 0
  138. thought = "WAKING UP..."
  139. choice = BoxSelector(bots).pick()
  140. if not choice:
  141. choice = 0 # BoB
  142. name = bots[choice]
  143. if name == "AIBoB":
  144. bot = AIBob() # BoB
  145. else:
  146. bot = AIAlice() #Alice
  147. move = bot.makeMove(win, None, food, snake) # first move
  148. moves += 1
  149. thought = text_to_thought(move)
  150. # build game
  151. while move != 27: # SPACEBAR
  152. win.border(0)
  153. win.addstr(0, 4, '| Moves: '+str(moves)+' - Max: '+str(max_moves)+' | Score: '+str(score)+' - Record: '+str(record)+' |')
  154. win.addstr(19, 4, '| '+str(name)+' -> GENERATION: '+str(evol)+' [IDEA: '+str(thought)+'] |')
  155. win.timeout(150 - int((len(snake)/5) + int(len(snake)/10)%120)) # if > length: > speed
  156. prevMove = move
  157. event = win.getch()
  158. move = bot.makeMove(win, prevMove, food, snake) # rest of movements
  159. moves += 1
  160. thought = text_to_thought(move)
  161. move = move if event == -1 else event
  162. if move != KEY_UP and move != KEY_DOWN and move != KEY_LEFT and move != KEY_RIGHT : # ANY KEY for pause
  163. if move == 27: # ESC
  164. breaked = ' GAME FINISHED: GOOD BYE! ;-)'
  165. win.addstr(9, 9, breaked)
  166. win.getch()
  167. curses.nocbreak()
  168. curses.echo()
  169. curses.endwin()
  170. break
  171. move = -1 # pause
  172. paused = ' GAME PAUSED: PRESS -SPACEBAR- TO RESTORE'
  173. win.addstr(9, 9, paused)
  174. while move != ord(' '):
  175. paused = ' ' # 42
  176. move = win.getch()
  177. win.addstr(9, 9, paused)
  178. win.addch(food[0], food[1], '*')
  179. move = prevMove
  180. continue
  181. if score >= record: # NEW record!
  182. record = score
  183. if moves >= max_moves: # NEW max moves!
  184. max_moves = moves
  185. snake.insert(0, [snake[0][0] + (move == KEY_DOWN and 1) + (move == KEY_UP and -1), snake[0][1] + (move == KEY_LEFT and -1) + (move == KEY_RIGHT and 1)])
  186. if snake[0][0] == 0:
  187. snake[0][0] = 18
  188. if snake[0][1] == 0:
  189. snake[0][1] = 58
  190. if snake[0][0] == 19:
  191. snake[0][0] = 1
  192. if snake[0][1] == 59:
  193. snake[0][1] = 1
  194. if snake[0] in snake[1:]: # GAME LOST ;-(
  195. win.addstr(9, 18, ' SORRY: GAME OVER !!!')
  196. win.addstr(19, 4, '| PyAISnake -> MUTATION: '+str(evol)+' [IDEA: TRYING AGAIN!] |')
  197. if score >= record: # NEW record!
  198. record = score
  199. if moves >= max_moves: # NEW max moves!
  200. max_moves = moves -1
  201. event = win.getch()
  202. time.sleep(2)
  203. evol += 1
  204. init(evol, record, max_moves)
  205. if snake[0] == food:
  206. food = []
  207. score += 1
  208. while food == []:
  209. food = [random.randint(1, 18), random.randint(1, 58)]
  210. if food in snake: food = []
  211. win.addch(food[0], food[1], '*')
  212. else:
  213. last = snake.pop()
  214. try:
  215. win.addch(last[0], last[1], ' ')
  216. except:
  217. pass
  218. try:
  219. win.addch(snake[0][0], snake[0][1], '#')
  220. except:
  221. pass
  222. init(0, 0, 0) # start a new GAME ;-)