123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-"
- """
- PyAISnake - 2018/2020 - by psy (epsylon@riseup.net)
- You should have received a copy of the GNU General Public License along
- with PyAISnake; if not, write to the Free Software Foundation, Inc., 51
- Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- """
- import curses, random, time
- from curses import KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN
- ######################################################################3
- # import AI models into sandbox
- from models.AIBob import AIBob
- from models.AIAlice import AIAlice
- bots = ['AIBoB','AIAlice']
- ######################################################################3
- class BoxSelector:
- def __init__(self, bots):
- self.TEXTBOX_WIDTH = 50
- self.TEXTBOX_HEIGHT = 6
- self.PAD_WIDTH = 400
- self.PAD_HEIGHT = 10000
- def pick(self):
- self._init_curses()
- self._create_pad()
- windows = self._make_textboxes()
- picked = self._select_textbox(windows)
- self._end_curses()
- return picked
- def _init_curses(self):
- self.stdscr = curses.initscr()
- curses.noecho()
- curses.cbreak()
- curses.curs_set(0)
- self.stdscr.keypad(1)
- curses.start_color()
- curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_GREEN)
- curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK)
- self.stdscr.bkgd(curses.color_pair(2))
- self.stdscr.refresh()
- def _end_curses(self):
- curses.nocbreak()
- self.stdscr.keypad(0)
- curses.echo()
- curses.endwin()
- def _create_pad(self):
- self.pad = curses.newpad(self.PAD_HEIGHT, self.PAD_WIDTH)
- self.pad.box()
- def _make_textboxes(self):
- maxy, maxx = self.stdscr.getmaxyx()
- windows = []
- i = 1
- for s in bots:
- windows.append(self.pad.derwin(self.TEXTBOX_HEIGHT,
- self.TEXTBOX_WIDTH, i, self.PAD_WIDTH//2-self.TEXTBOX_WIDTH//2))
- i += self.TEXTBOX_HEIGHT
- for k in range(len(windows)):
- windows[k].box()
- windows[k].addstr(4, 4, '{0:X} - {1}'.format(k, bots[k]))
- return windows
- def _center_view(self, window):
- cy, cx = window.getbegyx()
- maxy, maxx = self.stdscr.getmaxyx()
- self.pad.refresh(cy, cx, 1, maxx//2 - self.TEXTBOX_WIDTH//2, maxy-1, maxx-1)
- return (cy, cx)
- def _select_textbox(self, windows):
- topy, topx = self._center_view(windows[0])
- current_selected = 0
- last = 1
- top_textbox = windows[0]
- while True:
- windows[current_selected].bkgd(curses.color_pair(1))
- windows[last].bkgd(curses.color_pair(2))
- maxy, maxx = self.stdscr.getmaxyx()
- cy, cx = windows[current_selected].getbegyx()
- if ((topy + maxy - self.TEXTBOX_HEIGHT) <= cy):
- top_textbox = windows[current_selected]
- if topy >= cy + self.TEXTBOX_HEIGHT:
- top_textbox = windows[current_selected]
- if last != current_selected:
- last = current_selected
- topy, topx = self._center_view(top_textbox)
- c = self.stdscr.getch()
- if c == ord('0') or c == 258: # KEY_UP
- if current_selected >= len(windows)-1:
- current_selected = 0
- else:
- current_selected += 1
- elif c == ord('1') or c == 259: # KEY_DOWN
- if current_selected <= 0:
- current_selected = len(windows)-1
- else:
- current_selected -= 1
- elif c == ord('q') or c == 27: # ESC
- break
- elif c == curses.KEY_ENTER or c == 10:
- return int(current_selected)
- # translate a thought into a text
- def text_to_thought(move):
- if move == KEY_UP:
- thought = "UP"
- elif move == KEY_LEFT:
- thought = "LEFT"
- elif move == KEY_RIGHT:
- thought = "RIGHT"
- elif move == KEY_DOWN:
- thought = "DOWN"
- else:
- thought = "LEARNING..."
- return thought
- # create matrix (with some random init values)
- def init(evol, record, max_moves):
- curses.initscr()
- x_height = 0
- y_height = 20
- x_width = 60
- y_width = 0
- win = curses.newwin(y_height, x_width, x_height, y_width)
- win.keypad(1)
- curses.noecho()
- curses.curs_set(0)
- win.border(0)
- win.nodelay(1)
- food = [random.randint(1, 18), random.randint(1, 58)] # set resources into matrix
- win.addch(food[0], food[1], '*')
- p1_a1 = random.randint(1, 10)
- p1_a2 = random.randint(1, 10)
- p1_b1 = random.randint(1, 10)
- p1_b2 = random.randint(1, 10)
- p1_c1 = random.randint(1, 10)
- p1_c2 = random.randint(1, 10)
- snake = [[p1_a1,p1_a2], [p1_b1,p1_b2], [p1_c1,p1_c2]] # generate starting point
- startGame(win, food, snake, evol, record, max_moves) # start NEW GAME!
- # start a new game simulation
- def startGame(win, food, snake, evol, record, max_moves):
- moves = 0
- score = 0
- thought = "WAKING UP..."
- choice = BoxSelector(bots).pick()
- if not choice:
- choice = 0 # BoB
- name = bots[choice]
- if name == "AIBoB":
- bot = AIBob() # BoB
- else:
- bot = AIAlice() #Alice
- move = bot.makeMove(win, None, food, snake) # first move
- moves += 1
- thought = text_to_thought(move)
- # build game
- while move != 27: # SPACEBAR
- win.border(0)
- win.addstr(0, 4, '| Moves: '+str(moves)+' - Max: '+str(max_moves)+' | Score: '+str(score)+' - Record: '+str(record)+' |')
- win.addstr(19, 4, '| '+str(name)+' -> GENERATION: '+str(evol)+' [IDEA: '+str(thought)+'] |')
- win.timeout(150 - int((len(snake)/5) + int(len(snake)/10)%120)) # if > length: > speed
- prevMove = move
- event = win.getch()
- move = bot.makeMove(win, prevMove, food, snake) # rest of movements
- moves += 1
- thought = text_to_thought(move)
- move = move if event == -1 else event
- if move != KEY_UP and move != KEY_DOWN and move != KEY_LEFT and move != KEY_RIGHT : # ANY KEY for pause
- if move == 27: # ESC
- breaked = ' GAME FINISHED: GOOD BYE! ;-)'
- win.addstr(9, 9, breaked)
- win.getch()
- curses.nocbreak()
- curses.echo()
- curses.endwin()
- break
- move = -1 # pause
- paused = ' GAME PAUSED: PRESS -SPACEBAR- TO RESTORE'
- win.addstr(9, 9, paused)
- while move != ord(' '):
- paused = ' ' # 42
- move = win.getch()
- win.addstr(9, 9, paused)
- win.addch(food[0], food[1], '*')
- move = prevMove
- continue
- if score >= record: # NEW record!
- record = score
- if moves >= max_moves: # NEW max moves!
- max_moves = moves
- 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)])
- if snake[0][0] == 0:
- snake[0][0] = 18
- if snake[0][1] == 0:
- snake[0][1] = 58
- if snake[0][0] == 19:
- snake[0][0] = 1
- if snake[0][1] == 59:
- snake[0][1] = 1
- if snake[0] in snake[1:]: # GAME LOST ;-(
- win.addstr(9, 18, ' SORRY: GAME OVER !!!')
- win.addstr(19, 4, '| PyAISnake -> MUTATION: '+str(evol)+' [IDEA: TRYING AGAIN!] |')
- if score >= record: # NEW record!
- record = score
- if moves >= max_moves: # NEW max moves!
- max_moves = moves -1
- event = win.getch()
- time.sleep(2)
- evol += 1
- init(evol, record, max_moves)
- if snake[0] == food:
- food = []
- score += 1
- while food == []:
- food = [random.randint(1, 18), random.randint(1, 58)]
- if food in snake: food = []
- win.addch(food[0], food[1], '*')
- else:
- last = snake.pop()
- try:
- win.addch(last[0], last[1], ' ')
- except:
- pass
- try:
- win.addch(snake[0][0], snake[0][1], '#')
- except:
- pass
- init(0, 0, 0) # start a new GAME ;-)
|