免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2492 | 回复: 0

python新手,写的贪吃蛇多线程AI,有兴趣的可以看写,写的糟糕的地方也请指教 [复制链接]

论坛徽章:
2
申猴
日期:2014-07-17 10:05:182015年迎新春徽章
日期:2015-03-04 09:58:11
发表于 2015-03-08 13:08 |显示全部楼层
使用pygame
代码如下:
  1. #!/usr/bin/python
  2. """
  3.     a python snake AI, which use the A* algorithm
  4.     just for fun
  5.     you can see more details from this website.

  6.     http:@//inventwithpython.com/blog/2013/04/22/multithreaded-python-tutorial-with-threadworms/

  7. """

  8. import random, pygame, sys, threading, time
  9. import sys
  10. from pygame.locals import *
  11. from Queue import Queue

  12. #the food move direction
  13. moveLeft = True
  14. moveRight = False  
  15. moveUp = False  
  16. moveDown = False  

  17. MAXINT = 9999
  18. i =0

  19. # Setting up constants
  20. NUM_SNAKES = 3  # the number of snakes in the grid
  21. FPS = 30        # frames per second that the program runs
  22. CELL_SIZE = 40  # how many pixels wide and high each "cell" in the grid is
  23. CELLS_WIDE = 25 # how many cells wide the grid is
  24. CELLS_HIGH = 15 # how many cells high the grid is

  25. food_queue = Queue(1)   #when the Queue is not empty,means has created the food square,
  26.                         #else havn't createdd one
  27. HAS_FREE = True
  28. FREE_CELLS =CELLS_WIDE*CELLS_HIGH #free cell num
  29. FOOD_L =[None,None]

  30. GRID = []
  31. for x in range(CELLS_WIDE):
  32.     GRID.append([None] * CELLS_HIGH)

  33. GRID_LOCK = threading.Lock() # pun was not intended, when to set a square's color, you need lock the grid
  34. FOOD_LOCATION = threading.Event()

  35. # Constants for some colors.
  36. #             R    G    B
  37. WHITE     = (255, 255, 255)
  38. BLACK     = (  0,   0,   0)
  39. DARKGRAY  = ( 40,  40,  40)
  40. BGCOLOR = BLACK             # color to use for the background of the grid
  41. GRID_LINES_COLOR = DARKGRAY # color to use for the lines of the grid

  42. # Calculate total pixels wide and high that the full window is
  43. WINDOWWIDTH = CELL_SIZE * CELLS_WIDE
  44. WINDOWHEIGHT = CELL_SIZE * CELLS_HIGH

  45. UP = 'up'
  46. DOWN = 'down'
  47. LEFT = 'left'
  48. RIGHT = 'right'

  49. HEAD = 0
  50. BUTT = -1 # negative indexes count from the end, so -1 will always be the last index

  51. # A global variable that the Snake threads check to see if they should exit.
  52. SNAKE_RUNNING = True

  53. #Node will be used in findpath
  54. class Node():
  55.     """in the A* algorithm, f is current distance to the target position,
  56.     father is previous node,
  57.     g is the distance which the snake has passed.
  58.     curr_l is the snake's head postion (x, y)
  59.     """
  60.     def __init__(self, f=None, father=None, g=None, curr_l=None):
  61.         if f is not None:
  62.             self.f = f
  63.         else:
  64.             self.f = MAXINT

  65.         if g is not None:
  66.             self.g = g
  67.         else:
  68.             self.g = 0

  69.         if father is not None:
  70.             self.father = father
  71.         else:
  72.             self.father = None

  73.         if curr_l is not None:
  74.             self.curr_l = curr_l
  75.         else:
  76.             self.curr_l = None

  77. class Food(threading.Thread):
  78.     """the snake eat this objet"""
  79.     def __init__(self, color=None, maxsize=None):
  80.         global FREE_CELLS, FOOD_L
  81.         threading.Thread.__init__(self)
  82.         if color == None:
  83.             self.color = (255, 255, 255)
  84.         else:
  85.             self.color = color

  86.         if maxsize == None:
  87.             self.maxsize = 1
  88.         else:
  89.             self.maxsize = maxsize

  90.         self.body = []

  91.         GRID_LOCK.acquire()
  92.         #random the food's (x, y) location
  93.         while True:
  94.             startx = random.randint(0, CELLS_WIDE-1)
  95.             starty = random.randint(0, CELLS_HIGH-1)
  96.             if GRID[startx][starty] is None:
  97.                 break;
  98.         GRID[startx][starty] = self.color
  99.         FOOD_LOCATION.set()    #food position has been setted, notify the check_foodlocation func
  100.         
  101.         self.food_location = [ {'x':startx, 'y':starty} ]
  102.         FOOD_L[0] = self.food_location[0]['x']
  103.         FOOD_L[1] = self.food_location[0]['y']
  104.         self.body.insert(0, {'x':FOOD_L[0], 'y':FOOD_L[1]})
  105.         food_queue.put('has food') #when the food has been setted, put this word into food_queue to
  106.                                    #notify other thread function
  107.         GRID_LOCK.release()



  108.     def new_food(self):   #create a new food square if it has been eaten by single snake
  109.         global FREE_CELLS, HAS_FREE, FOOD_L, food_queue

  110.         GRID_LOCK.acquire()
  111.         while True:
  112.              startx = random.randint(0, CELLS_WIDE-1)
  113.              starty = random.randint(0, CELLS_HIGH-1)
  114.              if GRID[startx][starty] is None:
  115.                  break;
  116.         print 'food', startx, starty
  117.         GRID[startx][starty] = self.color
  118.         FOOD_LOCATION.set()
  119.         FREE_CELLS -= 1
  120.         if FREE_CELLS == 0:
  121.             HAS_FREE = False  #when all the gird has the color the produce need to be stopped
  122.         self.food_location = [ {'x':startx, 'y':starty} ]
  123.         FOOD_L[0] = self.food_location[0]['x']
  124.         FOOD_L[1] = self.food_location[0]['y']
  125.         food_queue.put('has food')
  126.         print 'new_food at [ %s , %s ]' %(FOOD_L[0], FOOD_L[1])
  127.         GRID_LOCK.release()

  128.     def run(self):
  129.         global SNAKE_RUNNING
  130.         while True:
  131.             if not SNAKE_RUNNING:
  132.                 print 'snakes terminate'
  133.                 return
  134.             if food_queue.empty():
  135.                 if HAS_FREE == True: # still some grid without color, create one
  136.                     self.new_food()
  137.                 else:
  138.                     SNAKE_RUNNING = False
  139.             time.sleep(0.01)
  140.                
  141. class Snake(threading.Thread): # "Thread" is a class in the "threading" module.
  142.     def __init__(self, name='Snake', thread_name=None, maxsize=None, color=None, speed=None):
  143.         # name can be used for debugging purposes. It will appear in any thrown exceptions so you can tell which thread crashed.
  144.         # maxsize is the length of the snake (in body segments).
  145.         # color is an RGB tuple for the snake. The darker shade is automatically calculated.
  146.         # speed is an integer of milliseconds the snake waits after moving once. 1000=move once a second, 0=move as fast as possible
  147.         global FREE_CELLS, FOOD_L
  148.         threading.Thread.__init__(self) # since we are overriding the Thread class, we need to first call its __init__() method.

  149.         self.name = name
  150.         self.thread_name = thread_name

  151.         self.maxsize = 1
  152.         self.OPEN = {}
  153.         self.CLOSE = {}
  154.         self.path = []
  155.         self.thread_name = thread_name


  156.         # Set the color to the parameter, or to a random color.
  157.         if color is None:
  158.             self.color = (random.randint(60, 255), random.randint(60, 255), random.randint(60, 255))
  159.         else:
  160.             self.color = color

  161.         # Set the speed to the parameter, or to a random number.
  162.         if speed is None:
  163.             self.speed = random.randint(20, 50) # wait time before movements will be between 0.2 and 0.5 seconds
  164.         else:
  165.             self.speed = speed

  166.         GRID_LOCK.acquire() # block until this thread can acquire the lock

  167.         while True:
  168.             startx = random.randint(0, CELLS_WIDE - 1)
  169.             starty = random.randint(0, CELLS_HIGH - 1)
  170.             if GRID[startx][starty] is None:
  171.                 break # we've found an unoccupied cell in the grid

  172.         print 'snake [ %s ] start at  (%s, %s)' %(self.thread_name, startx, starty)
  173.         GRID[startx][starty] = self.color # modify the shared data structure
  174.         FREE_CELLS = FREE_CELLS - self.maxsize

  175.         GRID_LOCK.release()

  176.         # The snake's body starts as a single segment, and keeps growing until it
  177.         # reaches full length. This makes setup easier.
  178.         self.body = [{'x': startx, 'y': starty}]
  179.         self.direction = random.choice((UP, DOWN, LEFT, RIGHT))

  180.         self.start_l = (startx, starty)
  181.         self.end_l = None
  182.         self.nextx = 999
  183.         self.nexty = 999


  184.     def run(self):
  185.         global  FOOD_L
  186.         isfirst = True
  187.         isrun = False
  188.         old_start_l = self.start_l
  189.         old_end_l = self.end_l
  190.         chthread_run = False
  191.         while True:
  192.             if chthread_run == False:
  193.                 t = threading.Thread(target=self.check_foodlocation)  #start another thread to check whether the
  194.                                                                       #food location has been changed (use A* algorithm)
  195.                 t.start()
  196.                 chthread_run = True

  197.             if not SNAKE_RUNNING:
  198.                 return # A thread terminates when run() returns.

  199.             # Randomly decide to change direction
  200.             #if random.randint(0, 100) < 20: # 20% to change direction
  201.             #    self.direction = random.choice((UP, DOWN, LEFT, RIGHT))
  202.             
  203.             GRID_LOCK.acquire()
  204.             if food_queue.empty():
  205.                 GRID_LOCK.release()  #release the grid lock and the let the food thread
  206.                                      #has access to get it and create a food on the free grid
  207.                 time.sleep(0.01)
  208.             else:
  209.                 start_l = (self.body[0]['x'], self.body[0]['y'])
  210.                 end_l = (FOOD_L[0], FOOD_L[1])

  211.                 self.start_l = start_l
  212.                 self.end_l = end_l
  213.                 old_start_l = start_l
  214.                 old_end_l = end_l

  215.                 print "thread [ %s ] start_l = %s" %(self.thread_name, str(start_l))
  216.                 print "thread [ %s ] end_l = %s" %(self.thread_name, str(end_l))

  217.                 if isfirst == True:  
  218.                     self.end_l = end_l
  219.                     self.OPEN.clear()  
  220.                     self.CLOSE.clear()
  221.                     del self.path[:]
  222.                     result = self.find_path(start_l, end_l)
  223.                     for path in self.path:
  224.                         self.reverse_time = 0
  225.                         print path,
  226.                     if result == -1:    #can't find the path to reache the food location
  227.                         if self.reverse_time == 0:
  228.                             self.body.reverse()     #reverse the snake body(but it may be wrong when
  229.                             self.reverse_time = 1   #there is only a litte free grid left.
  230.                     isfirst = False

  231.                 if self.end_l != end_l:    #the food location has been changed
  232.                     FOOD_LOCATION.set()
  233.                     GRID_LOCK.release()
  234.                    # time.sleep(0.01)
  235.                     continue
  236.                   
  237.                 if len(self.path) > 1:
  238.                     next = self.path[1]
  239.                     self.nextx = next[0]
  240.                     self.nexty = next[1]

  241.                 if len(self.path) == 1:
  242.                     next = self.path[0]
  243.                     self.nextx = next[0]
  244.                     self.nexty = next[1]
  245.                     

  246.                 if GRID[self.nextx][self.nexty] is not None:
  247.                     if GRID[self.nextx][self.nexty] != WHITE:  #the location which the snake wanna move
  248.                                                                #is in the other's snake's body
  249.                                                                #WHITE means food
  250.                         print (self.nextx, self.nexty)
  251.                         print GRID[self.nextx][self.nexty]
  252.                         print self.thread_name, " it's not None, caculate again", (self.nextx, self.nexty)
  253.                         FOOD_LOCATION.set()       #update the path, the older one can't pass
  254.                         GRID_LOCK.release()
  255.                         time.sleep(0.01)
  256.                         continue
  257.                     else:
  258.                         print 'get the food'
  259.                         self.maxsize += 1
  260.                         self.OPEN.clear()
  261.                         self.CLOSE.clear()
  262.                         food_queue.get()   #the food has beend eaten by this snake thread

  263.                 if len(self.path) > 0:   #self.paht record the path to the food location
  264.                     #print 'delete self.path[0]' ,self.path[0]
  265.                     self.path.pop(0) #delete head

  266.                   
  267.                 GRID[self.nextx][self.nexty] = self.color # update the GRID state
  268.                 self.body.insert(0, {'x': self.nextx, 'y': self.nexty}) # update this snake's own state
  269.                 self.start_l = (self.nextx, self.nexty)

  270.                 if food_queue.empty():
  271.                     del self.path[:]

  272.                 # Check if we've grown too long, and cut off tail if we have.
  273.                 # This gives the illusion of the snake moving.
  274.                 if len(self.body) > self.maxsize:
  275.                     GRID[self.body[BUTT]['x']][self.body[BUTT]['y']] = None # update the GRID state
  276.                     del self.body[BUTT]                                     #it's like the snake has moved to the next position
  277.                 pygame.time.wait(self.speed)
  278.                 GRID_LOCK.release()
  279.                 time.sleep(0.01)

  280.     def check_foodlocation(self):  #check if food position has changed
  281.         while True:
  282.             global food_queue, FOOD_L, FOOD_LOCATION
  283.             FOOD_LOCATION.wait()       #all the snake check path thread activate by the FOOD_LOCATION, when
  284.                                        #the new food event come up
  285.             FOOD_LOCATION.clear()
  286.             if food_queue.full() and self.end_l is not None:
  287.                 GRID_LOCK.acquire()   #when to calaute the path again, you need the grid to
  288.                                       #lock all the grid first
  289.                 self.OPEN.clear()
  290.                 self.CLOSE.clear()
  291.                 del self.path[:]
  292.                 self.end_l = (FOOD_L[0], FOOD_L[1])
  293.                 print 'the food position has been changed to [%s, %s]' %(FOOD_L[0], FOOD_L[1])
  294.                 result = self.find_path(self.start_l, self.end_l) #find the path to food again
  295.                 if result == -1:
  296.                     self.body.reverse()
  297.                 GRID_LOCK.release()
  298.             

  299.     def find_path(self, start_l, end_l):    #use the A* algorithm
  300.         node = Node()
  301.         node.curr_l = start_l
  302.         node.father = None
  303.         self.OPEN[start_l] = node
  304.         isfirst = True
  305.         while True:
  306.             if isfirst == True:
  307.                 dict_item = self.OPEN.pop(start_l, None)
  308.                 if dict_item is not None:
  309.                     self.CLOSE[start_l] = dict_item
  310.                     self.isaround_ok(start_l, end_l)
  311.                 else:
  312.                     print 'OPEN is None'
  313.                 isfirst = False
  314.             else:
  315.                 if len(self.OPEN) == 0:
  316.                     print "can't find distination"
  317.                     return -1

  318.                 curr_l = self.find_min()
  319.                 #print curr_l,'************************************'
  320.                 dict_item = self.OPEN.pop(curr_l, None)
  321.                 if dict_item is not None:
  322.                     self.CLOSE[curr_l] = dict_item
  323.                 if curr_l == end_l:
  324.                     print 'ok find it'
  325.                     self.get_path(curr_l)
  326.                     return 0
  327.                 else:
  328.                     self.isaround_ok(curr_l, end_l)


  329.     def isaround_ok(self, start_l, end_l): # to find the next position, but it must in
  330.                                            # the range of grid, and must not in CLOSE dict
  331.                                            # any details please see the principle of A star algorithm
  332.         start_x = start_l[0]
  333.         start_y = start_l[1]
  334.         end_x = end_l[0]
  335.         end_y = end_l[1]
  336.    
  337.         father = self.CLOSE[start_l]
  338.         location = []
  339.         if start_x-1 != -1:
  340.             location.append( (start_x-1, start_y) )
  341.         
  342.         if start_x+1 != CELLS_WIDE:
  343.             location.append( (start_x+1, start_y) )

  344.         if start_y-1 != -1:
  345.             location.append( (start_x, start_y-1) )
  346.         
  347.         if start_y+1 != CELLS_HIGH:
  348.             location.append( (start_x, start_y+1) )

  349.         if len(location) == 0:
  350.             return;
  351.       
  352.         for curr_l in location:
  353.             x = curr_l[0]
  354.             y = curr_l[1]
  355.             #print (x, y)
  356.             if curr_l not in self.CLOSE.keys() and (GRID[x][y] == WHITE or GRID[x][y] == None):
  357.                 if father is not None:
  358.                     g = father.g+1
  359.                 else:
  360.                     g = 1
  361.                 h = self.judge_dis(curr_l, end_l)
  362.                 f_new = g+h
  363.                 if curr_l in self.OPEN.keys():
  364.                     if f_new < father.f:
  365.                         self.OPEN[curr_l].father = father
  366.                         self.OPEN[curr_l].f = f_new
  367.                         self.OPEN[curr_l].g = g

  368.                 #not in OPEN[]
  369.                 else:
  370.                     node = Node()
  371.                     node.curr_l = curr_l
  372.                     node.g = g
  373.                     node.father = father
  374.                     node.f = f_new
  375.                     self.OPEN[curr_l] = node

  376.     def find_min(self):
  377.         min = 9999
  378.         key = None
  379.         for eachkey in self.OPEN.keys():
  380.             f = self.OPEN[eachkey].f
  381.             if f<min:
  382.                 key = eachkey
  383.                 min = f
  384.         return key

  385.     def judge_dis(self, start_l, end_l):
  386.         start_x = start_l[0]
  387.         start_y = start_l[1]
  388.         end_x = end_l[0]
  389.         end_y = end_l[1]
  390.         h = abs(end_x -start_x) + abs(end_y -start_y)
  391.         return h
  392.    
  393.     def get_path(self, curr_l):
  394.         self.path.append(curr_l)
  395.         father = self.CLOSE[curr_l].father
  396.         while father is not None:
  397.             curr_l = father.curr_l
  398.             self.path.append(curr_l)
  399.             father = self.CLOSE[curr_l].father
  400.         self.path.reverse()

  401. def main():
  402.     global FPSCLOCK, DISPLAYSURF, FREE_CELLS

  403.     # Draw some walls on the grid

  404.     # Pygame window set up.
  405.     pygame.init()
  406.     FPSCLOCK = pygame.time.Clock()
  407.     DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
  408.     pygame.display.set_caption('Threadsnake')

  409.     # Create the snake objects.

  410.     snakes = [] # a list that contains all the snake objects
  411.     j = 0;
  412.     for i in range(NUM_SNAKES):
  413.         j += 1
  414.         snakes.append(Snake('snake', j, None, None, None) )
  415.         snakes[-1].start() # Start the snake code in its own thread.
  416.    
  417.     #To creat the food(the white square is the food)
  418.     food = Food()
  419.     food.start()


  420.     while True: # main game loop
  421.         handleEvents()
  422.         drawGrid()

  423.         pygame.display.update()
  424.         FPSCLOCK.tick(FPS)


  425. def handleEvents():
  426.     # The only event we need to handle in this program is when it terminates.
  427.     global SNAKE_RUNNING, moveLeft, moveRight, moveUp, moveDown

  428.     for event in pygame.event.get(): # event handling loop
  429.         if event.type == QUIT:
  430.             SNAKE_RUNNING = False # Setting this to False tells the Snake threads to exit.
  431.             pygame.quit()
  432.             sys.exit()

  433.         if event.type == KEYDOWN:  
  434.             print 'Kyedown !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
  435.             if event.key == K_ESCAPE:
  436.                SNAKE_RUNNING = False # Setting this to False tells the Snake threads to exit.
  437.                pygame.quit()
  438.                sys.exit()
  439.             if event.key == K_LEFT and moveRight==False:  
  440.                 moveLeft = True  
  441.                 moveRight = False  
  442.                 moveUp = False  
  443.                 moveDown = False  
  444.             if event.key == K_RIGHT and moveLeft==False:  
  445.                 moveLeft = False  
  446.                 moveRight = True  
  447.                 moveUp = False  
  448.                 moveDown = False  
  449.             if event.key == K_UP and moveDown==False:  
  450.                 moveLeft = False  
  451.                 moveRight = False  
  452.                 moveUp = True  
  453.                 moveDown = False  
  454.             if event.key == K_DOWN and moveUp==False:  
  455.                 moveLeft = False  
  456.                 moveRight = False  
  457.                 moveUp = False  
  458.                 moveDown = True  
  459.          

  460. def drawGrid():
  461.     # Draw the grid lines.
  462.     DISPLAYSURF.fill(BGCOLOR)
  463.     for x in range(0, WINDOWWIDTH, CELL_SIZE): # draw vertical lines
  464.         pygame.draw.line(DISPLAYSURF, GRID_LINES_COLOR, (x, 0), (x, WINDOWHEIGHT))
  465.     for y in range(0, WINDOWHEIGHT, CELL_SIZE): # draw horizontal lines
  466.         pygame.draw.line(DISPLAYSURF, GRID_LINES_COLOR, (0, y), (WINDOWWIDTH, y))

  467.     # The main thread that stays in the main loop (which calls drawGrid) also
  468.     # needs to acquire the GRID_LOCK lock before modifying the GRID variable.
  469.     GRID_LOCK.acquire()

  470.     for x in range(0, CELLS_WIDE):
  471.         for y in range(0, CELLS_HIGH):
  472.             if GRID[x][y] is None:
  473.                 continue # No body segment at this cell to draw, so skip it

  474.             color = GRID[x][y] # modify the GRID data structure

  475.             # Draw the body segment on the screen
  476.             darkerColor = (max(color[0] - 50, 0), max(color[1] - 50, 0), max(color[2] - 50, 0))
  477.             pygame.draw.rect(DISPLAYSURF, darkerColor, (x * CELL_SIZE,     y * CELL_SIZE,     CELL_SIZE,     CELL_SIZE    ))
  478.             pygame.draw.rect(DISPLAYSURF, color,       (x * CELL_SIZE + 4, y * CELL_SIZE + 4, CELL_SIZE - 8, CELL_SIZE - 8))

  479.     GRID_LOCK.release() # We're done messing with GRID, so release the lock.


  480. def setGridSquares(squares, color=(192, 192, 192)):
  481.     squares = squares.split('\n')
  482.     if squares[0] == '':
  483.         del squares[0]
  484.     if squares[-1] == '':
  485.         del squares[-1]

  486.     GRID_LOCK.acquire()
  487.     for y in range(min(len(squares), CELLS_HIGH)):
  488.         for x in range(min(len(squares[y]), CELLS_WIDE)):
  489.             if squares[y][x] == ' ':
  490.                 GRID[x][y] = None
  491.             elif squares[y][x] == '.':
  492.                 pass
  493.             else:
  494.                 GRID[x][y] = color
  495.     GRID_LOCK.release()


  496. if __name__ == '__main__':
  497.     main()
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP