class Game: # Define some colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) BLUE = (0,0,255) GREEN = (0,225,0) YELLOW = (225,225,0) WALL_SCORE = -20 MARGIN = 1 # how long between each two squares offsetUp = 40 + randint(60,100)# offset for space to show score and time on the top of canvas offsetLeft = randint(60,100) offsetRight = randint(60,100) offsetDown = randint(60,100) moveLs = collections.Counter() # Initialize the setting of world def __init__(self,grids_x=10,grids_y=10,grid_width=20,grid_height=20,obstaclesPer=20,obstaclesMap=None): self.GRIDS_X = grids_x # how many squares in x direction self.GRIDS_Y = grids_y # how many squares in y direction self.WIDTH = grid_width # square width self.HEIGHT = grid_height # square height self.OBSTACLSNUM = int(float(grids_x)*grids_y*obstaclesPer/100)# how many obstacles in the setting self.grid = [ [0 for x in range(grids_x)] for y in range(grids_y) ] self.gridValue = np.asarray( [ [0 for x in range(self.GRIDS_X)] for y in range(self.GRIDS_Y) ] ) self.score = 0 self.drone = None self.target = None self.obstaclesMap = obstaclesMap self.obstaclesLs = [] self.generateStartPoint() self.updateGrid() pygame.init() clock = pygame.time.Clock() clock.tick(60) self.screen = self.canvas() self.default_font = pygame.font.Font(None, 28) self.moveLs = collections.Counter() def generateObstacles(self): self.obstacleSet = set() # generate obstacles if self.obstaclesMap is not None: map_ob = open(self.obstaclesMap, 'r') for line in map_ob: loc = line.split(',') x,y = int(loc[0]),int(loc[1]) self.obstacleSet.add( (x,y) ) else: while len(self.obstacleSet) < self.OBSTACLSNUM: self.obstacleSet.add( (randint(0,self.GRIDS_X-1),randint(0,self.GRIDS_Y-1)) ) for ob in self.obstacleSet: x,y = ob[0],ob[1] block = Obstacle(x,y) self.obstaclesLs.append(block) self.grid[y][x] = block def generateDrone(self): while True: x,y=randint(0,self.GRIDS_X-1),randint(0,self.GRIDS_Y-1) if self.grid[y][x] == 0: self.drone = Drone(x,y) self.grid[y][x] = self.drone break def generateTarget(self): while True: x,y=randint(2,self.GRIDS_X-3),randint(2,self.GRIDS_Y-3) if self.grid[y][x] == 0: self.target = Target(x,y,True) self.grid[y][x] = self.target break def copmuteObstacleReward(self): for obstacle in self.obstaclesLs: self.gridValue[obstacle.y][obstacle.x] += obstacle.reward def computeTargetReward(self,gridValue): target = self.target rewardRange = target.rewardRange x, y = self.target.x, self.target.y startX, endX = x - rewardRange, x + rewardRange startY, endY = y - rewardRange, y + rewardRange # target will compute correspond reward matrix target.computeRewardMatrix() rewardMatrix = target.rewardMatrix[1] for row_grid,row_reward in zip(range(startY,endY+1),range(2*rewardRange+1)): if row_grid < 0 or row_grid >=self.GRIDS_Y: continue for col_grid, col_reward in zip(range(startX,endX+1),range(2*rewardRange+1)): if col_grid < 0 or col_grid >=self.GRIDS_X: continue gridValue[row_grid][col_grid] += rewardMatrix[row_reward][col_reward] def computeValue(self): # calculate obstacle value self.gridValue = np.asarray( [ [0 for x in range(self.GRIDS_X)] for y in range(self.GRIDS_Y) ] ) self.copmuteObstacleReward() self.computeTargetReward(self.gridValue) # randomly generate strat point on the grid for every stuff def generateStartPoint(self): self.generateObstacles() self.generateDrone() self.generateTarget() self.computeValue() def drawSquares(self,screen): white = np.asarray(list(self.WHITE)) yellow = np.asarray(list(self.YELLOW)) gradient = np.subtract(yellow,white) for row in range(self.GRIDS_Y): for col in range(self.GRIDS_X): # print (row,col,self.grid[row][col]) DistLeft = (self.MARGIN + self.WIDTH) * col + self.MARGIN DistUp = (self.MARGIN + self.HEIGHT) * row + self.MARGIN name = 'empty' if not self.grid[row][col] else self.grid[row][col].name if not self.drone.checkExploration(col,row): color = self.BLACK if name is 'target': color = self.GREEN else: if self.grid[row][col] == 0: # print (row,col,self.grid[row][col]) color = self.WHITE if self.target.isReward(col,row): w = self.target.getReward(col,row)/self.target.maxReward # print (self.target.getReward(col,row),self.target.maxReward) color = tuple(np.add(white,np.multiply(gradient,w))) else: if name is 'drone': color = self.BLUE elif name is 'target': color = self.GREEN else: color = self.RED pygame.draw.rect(self.screen,color,[DistLeft+self.offsetLeft,DistUp+self.offsetUp,self.WIDTH,self.HEIGHT]) def draw_text(self, text, font, surface, x, y, main_color, background_color=None): textobj = font.render(text, True, main_color, background_color) textrect = textobj.get_rect() textrect.centerx = x textrect.centery = y surface.blit(textobj, textrect) def canvas(self): windowSize = [ (self.GRIDS_X+self.MARGIN)*self.WIDTH+self.offsetLeft+self.offsetRight, (self.GRIDS_Y+self.MARGIN)*self.HEIGHT+self.offsetUp+self.offsetDown] screen = pygame.display.set_mode(windowSize) pygame.display.set_caption("Awesome drone") # self.generateStartPoint() return screen def drawScore(self,screen,default_font,elapsed,timeEnd): self.drawSquares(self.screen) self.draw_text('points: {}'.format(self.score), default_font, self.screen, (self.GRIDS_X+self.MARGIN)*self.WIDTH*1/5, 20, self.GREEN) self.draw_text('time left: {}'.format(int(timeEnd-elapsed)), default_font, self.screen, (self.GRIDS_X+self.MARGIN)*self.WIDTH*4/5 , 20, self.GREEN) #after every stuff move, update the information of grid and value of grid def updateGrid(self): # remove original location target, drone = self.target, self.drone # undo last move # print (self.grid) self.grid[target.lastY][target.lastX] = 0 self.grid[drone.lastY][drone.lastX] = 0 # rebuild obstacles for obstacle in self.obstaclesLs: # self.grid[obstacle.lastY][obstacle.lastX] = 0 self.grid[obstacle.y][obstacle.x] = obstacle # print (self.grid,'\n\n') self.grid[target.y][target.x] = target # for Target self.grid[drone.y][drone.x] = drone # for drone self.computeValue() # compue grid value self.score += self.gridValue[self.drone.y][self.drone.x] def hitWall(self): x, y = self.drone.x, self.drone.y if x<0 or y<0 or x>self.GRIDS_X-1 or y>self.GRIDS_Y-1: return True else: return False ''' decide the preference move for target ''' def targetMove(self): a = self.target.move(self.GRIDS_X,self.GRIDS_Y) # a = self.target.preferenceMove(self.GRIDS_X,self.GRIDS_Y) self.moveLs[a] += 1 self.updateGrid() def eventHandler(self): for event in pygame.event.get(): # If user clicked close if event.type == pygame.QUIT: return True if event.type == pygame.KEYDOWN: self.drone.controlMove(event) if self.hitWall(): self.drone.undo() self.score += self.WALL_SCORE self.targetMove() self.updateGrid() return False def eventWait(self): # If user clicked close event = pygame.event.wait() if event.type == pygame.KEYDOWN: self.drone.controlMove(event) if self.hitWall(): self.drone.undo() self.score += self.WALL_SCORE self.targetMove() self.updateGrid() return False else: self.updateGrid() return True def drawCanvas(self,i,iters): self.screen.fill(self.BLACK) self.drawScore(self.screen,self.default_font,i,iters) pygame.display.flip() # get every possible next state and reward def getVisionLimitedNextStateAndReward(self,state,action): self.drone.backUp() self.drone.x, self.drone.y = state[0] self.drone.AImove(action) droneState = (self.drone.x, self.drone.y) nextStates = [] rewards = [] for act in self.target.possibleActions(self.GRIDS_X,self.GRIDS_Y): self.target.AImove(act) self.computeValue() reward = self.gridValue[self.drone.y][self.drone.x] if (self.drone.x,self.drone.y) not in self.drone.backExp and \ (self.drone.x,self.drone.y) in self.obstacleSet: reward += 50 state = [droneState, (self.target.x,self.target.y), (self.target.face, self.target.faceAngle)] nextStates.append(state) rewards.append(reward) self.target.undo() # self.drone.undo() self.drone.recoverBackUp() return nextStates,rewards def getVisionLimitedSuccessors(self,state,action): self.drone.backUp() self.drone.x, self.drone.y = state[0] self.drone.AImove(action) droneState = (self.drone.x, self.drone.y) successors = [] nextStates = [] rewards = [] for act in self.target.possibleActions(self.GRIDS_X,self.GRIDS_Y): self.target.AImove(act) self.computeValue() reward = self.gridValue[self.drone.y][self.drone.x] if (self.drone.x,self.drone.y) not in self.drone.backExp and \ (self.drone.x,self.drone.y) in self.obstacleSet: reward += 50 nextState = [droneState, (self.target.x,self.target.y), (self.target.face, self.target.faceAngle)] nextStates.append(nextState) probability = self.getPossibility(state,action,nextState) successor = [nextState, reward, probability] successors.append(successor) rewards.append(reward) self.target.undo() # self.drone.undo() self.drone.recoverBackUp() return successors def faceApproximation(self,state): targetPos = state[0] res = 0 if abs(targetPos[0])<=3 and abs(targetPos[1])<=3: res = sum(state[1]) return res # local approximation of relative loaction of target from (-n~n,-m~m) to (-10~10,-10~10) def targetApproximation(self,targetPos): # maintain exactly same relative position within (-5~5,-5~5), it is close to drone which is important # goal is to convert (-originalXRange~originalXRange,-originalYRange~originalYRange) to (-10~10,-10~10) originalXRange = self.GRIDS_X originalYRange = self.GRIDS_Y # local approximation for x if abs(targetPos[0])<=5: newX = targetPos[0] else: newX = 6 if targetPos[0]>0 else -6 # local approximation for y if abs(targetPos[1])<=5: newY = targetPos[1] else: newY = 6 if targetPos[1]>0 else -6 return (round(newX),round(newY)) # local approximate of location of obstacles from 5*5 to 3*3 def obstacleApproximation(self,obstaclePos): # local approximation for x if obstaclePos[0]<0: newX = -1 elif obstaclePos[0]==0: newX = 0 else: newX = 1 # local approximation for y if obstaclePos[1]<0: newY = -1 elif obstaclePos[1]==0: newY = 0 else: newY = 1 return(newX,newY) ##################### SARS structure ##################### # return state with location of drone and target [ droneLocation, targetLocatoin, face and faceangle ] def getState(self): return [(self.drone.x,self.drone.y),(self.target.x,self.target.y) ,( self.target.face, self.target.faceAngle)] # state become [relative position from drone to target, target's face situation, relative position to obstacles within 2 block distance] def getState2(self): state = [] # relative position from drone to target state.append((self.target.x-self.drone.x,self.target.y-self.drone.y)) # target's face situation state.append((self.target.face, self.target.faceAngle)) # relative position to obstacles within 2 block distance for obstacle in self.obstaclesLs: if abs(obstacle.y-self.drone.y)<=2 and abs(obstacle.x-self.drone.x)<=2: state.append((obstacle.x-self.drone.x,obstacle.y-self.drone.y)) return state def getLocalApporximationState(self): state = self.getState2() newState = [] newState.append(self.targetApproximation(state[0])) newState.append(self.faceApproximation(state)) for i in range(2,len(state)): newPos = self.obstacleApproximation(state[i]) if newPos not in newState: newState.append(newPos) return newState # return possible next action def getAction(self,state): self.drone.backUp() self.drone.x, self.drone.y = state[0] actions = self.drone.possibleActions(self.GRIDS_X,self.GRIDS_Y) self.drone.recoverBackUp() return actions # drone's move is for sure. assume target move randomly def getPossibility(self,state,action,nextState): return 1.0/len(self.target.possibleActions(self.GRIDS_X,self.GRIDS_Y)) # return [ nextState, reward, probability] def getSuccessors(self,state,action): self.drone.backUp() self.drone.x, self.drone.y = state[0] self.target.backUp() self.target.x, self.target.y = state[1] self.target.face, self.target.faceAngle= state[2] self.drone.AImove(action) droneState = (self.drone.x, self.drone.y) successors = [] nextStates = [] rewards = [] for act in self.target.possibleActions(self.GRIDS_X,self.GRIDS_Y): self.target.AImove(act) self.computeValue() reward = self.gridValue[self.drone.y][self.drone.x] nextState = [droneState, (self.target.x,self.target.y), (self.target.face, self.target.faceAngle)] probability = self.getPossibility(state,action,nextState) successor = [nextState, reward, probability] successors.append(successor) self.target.undo() # self.drone.undo() self.drone.recoverBackUp() self.target.recoverBackUp() return successors # get every possible next state and reward def getNextStateAndReward(self,state,action): self.drone.backUp() self.drone.x, self.drone.y = state[0] self.drone.AImove(action) droneState = (self.drone.x, self.drone.y) nextStates = [] rewards = [] for act in self.target.possibleActions(self.GRIDS_X,self.GRIDS_Y): self.target.AImove(act) self.computeValue() reward = self.gridValue[self.drone.y][self.drone.x] state = [droneState, (self.target.x,self.target.y), (self.target.face, self.target.faceAngle)] nextStates.append(state) rewards.append(reward) self.target.undo() # self.drone.undo() self.drone.recoverBackUp() return nextStates,rewards # move the drone and return the reward you get def moveAction(self,action): self.drone.AImove(action) self.targetMove() self.updateGrid() return self.gridValue[self.drone.y][self.drone.x] # return the reward at given state at current time def getReward(self,state): loc = state[0] return self.gridValue[loc[1]][loc[0]] # return gridValue which store all rewards at current time def getGrid(self): return self.gridValue # return the region that drone already explored def getExploredArea(self): return self.drone.exploredSet # return the cumulative score def getScore(self): return self.score def getObstaclesAronud(self): xStart = 0 if self.drone.x-2<0 else self.drone.x-2 xEnd = self.GRIDS_X-1 if self.drone.x+2>self.GRIDS_X-1 else self.drone.x+2 yStart = 0 if self.drone.y-2<0 else self.drone.y-2 yEnd = self.GRIDS_Y-1 if self.drone.y+2>self.GRIDS_Y-1 else self.drone.y+2 obstaclesAround = [] #print (xStart,xEnd,yStart,yEnd) for x in range(xStart,xEnd+1): for y in range(yStart,yEnd+1): name = 'empty' if not self.grid[y][x] else self.grid[y][x].name if name == 'obstacle': distance = abs(x-self.drone.x)+abs(y-self.drone.y) obstaclesAround.append([(x-self.drone.x,y-self.drone.y),distance]) return obstaclesAround # return reward matrix with assigned droneVal which indicate the location of drone def getRewardMatrix(self,droneVal): res = np.asarray( [ [0 for x in range(self.GRIDS_X)] for y in range(self.GRIDS_Y) ] ) self.computeTargetReward(res) for obstacle in self.obstaclesLs: if abs(obstacle.y-self.drone.y)<=2 and abs(obstacle.x-self.drone.x)<=2: res[obstacle.y][obstacle.x] += obstacle.reward res[self.drone.y][self.drone.x] = droneVal return res
def play_game(player, entities, world_map, message_log, game_state, current_enemy, con, panel, constants): # print("entities: ") # for entity in entities: # print(entity.name) current_map = world_map.maps[world_map.x][world_map.y] # entities = current_map.entities # print("entities: ") # for entity in entities: # print(entity.name) fov_recompute = True fov_map = initialize_fov(current_map) key = tcod.Key() mouse = tcod.Mouse() previous_game_state = game_state targeting_item = None player_target = Target(0,0) #main game loop while not tcod.console_is_window_closed(): #updates the key and mouse variables with any key or mouse events tcod.sys_check_for_event(tcod.EVENT_KEY_PRESS, key, mouse) if fov_recompute: fov_map = initialize_fov(current_map) recompute_fov(fov_map, player, constants['fov_radius'], entities, constants['fov_light_walls'], constants['fov_algorithm']) render_all(con, panel, entities, player, current_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['bar_width'], constants['panel_height'], constants['panel_y'], constants['colors'], game_state, current_enemy, player_target) tcod.console_flush() clear_all(con, entities) action = handle_keys(key, game_state) move = action.get('move') exit = action.get('exit') fullscreen = action.get('fullscreen') pickup = action.get('pickup') show_inventory = action.get('show_inventory') inventory_index = action.get('inventory_index') drop_inventory = action.get('drop_inventory') look = action.get('look') move_target = action.get('move_target') target_selected = action.get('target_selected') player_turn_results = [] #Current errors: # (FIXED) x and y are being set to default make_map values in opposite moves # diagonal moves off the edge of the map cause a crash # !!!FOCUS!!! entity lists are not attached to maps, so buildings remain while entities refresh # Need to readd player to returning maps #!!!!!!!!! #WHEN A USER SAVES AND LOADS ON A GAME_MAP, A PERMANENT "DUMB" COPY OF #THEIR BODY IS LEFT AT THE POSITION THEY SAVED AND LOADED #UPON SUBSEQUENT RETURNS TO THE MAP # #THE GLITCH ONLY OCCURS WHEN THE USER LEAVES THE MAP #SOMEHOW, PLAYER IS KEPT IN ENTITIES OVER TRANSITIONS #Each gamemap needs a copy of player? (maybe) #If you save and load multiple times on one map before leaving it, the #dumb copy spawns at the first position you saved and loaded at. #!!!!!!!!! if move and game_state == GameStates.PLAYER_TURN: dx, dy = move destination_x = player.x + dx destination_y = player.y + dy if destination_x == current_map.width: if world_map.x < 9: world_map.move_to(world_map.x+1, world_map.y, constants, player) current_map = world_map.maps[world_map.x][world_map.y] entities = current_map.entities destination_x = 0 dx = 0 player.x = 0 player.y = destination_y else: destination_x = player.x destination_y = player.y message_log.add_message(Message('You can\'t go that way', tcod.blue)) print(str(world_map.x)+" ,"+str(world_map.y)) elif destination_y == current_map.height: if world_map.y > 0: world_map.move_to(world_map.x, world_map.y-1, constants, player) current_map = world_map.maps[world_map.x][world_map.y] entities = current_map.entities destination_y = 0 dy = 0 player.y = 0 player.x = destination_x else: destination_x = player.x destination_y = player.y message_log.add_message(Message('You can\'t go that way', tcod.blue)) print(str(world_map.x)+" ,"+str(world_map.y)) elif destination_x == -1: if world_map.x > 0: world_map.move_to(world_map.x-1, world_map.y, constants, player) current_map = world_map.maps[world_map.x][world_map.y] entities = current_map.entities destination_x = current_map.width-1 dx = 0 player.x = current_map.width-1 player.y = destination_y else: destination_x = player.x destination_y = player.y message_log.add_message(Message('You can\'t go that way', tcod.blue)) print(str(world_map.x)+" ,"+str(world_map.y)) elif destination_y == -1: if world_map.y < 9: world_map.move_to(world_map.x, world_map.y+1, constants, player) current_map = world_map.maps[world_map.x][world_map.y] entities = current_map.entities destination_y = current_map.height-1 dy = 0 player.y = current_map.height-1 player.x = destination_x else: destination_x = player.x destination_y = player.y message_log.add_message(Message('You can\'t go that way', tcod.blue)) print(str(world_map.x)+" ,"+str(world_map.y)) if not current_map.is_blocked(destination_x, destination_y): target = get_blocking_entities_at_location(entities, destination_x, destination_y) if target: if target.fighter and target != player: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) elif target.breakable: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN elif pickup and game_state == GameStates.PLAYER_TURN: for entity in entities: if entity.item and entity.x == player.x and entity.y == player.y: pickup_results = player.inventory.add_item(entity) player_turn_results.extend(pickup_results) break else: message_log.add_message(Message('There is nothing here to pick up', tcod.yellow)) if show_inventory: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY if drop_inventory: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY if look: previous_game_state = game_state player_target.set(player.x, player.y) game_state = GameStates.LOOKING if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len(player.inventory.items): item = player.inventory.items[inventory_index] if game_state == GameStates.SHOW_INVENTORY: player_turn_results.extend(player.inventory.use(item, entities = entities, fov_map = fov_map)) elif game_state == GameStates.DROP_INVENTORY: player_turn_results.extend(player.inventory.drop_item(item)) if exit: if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.LOOKING): game_state = previous_game_state elif game_state == GameStates.TARGETING: player_turn_results.append({'targeting_cancelled': True}) else: save_game(player, entities, world_map, message_log, game_state, current_enemy) return True if fullscreen: tcod.console_set_fullscreen(not tcod.console_is_fullscreen()) if game_state == GameStates.TARGETING: if move_target: dx, dy = move_target player_target.move(dx, dy) if target_selected: item_use_results = player.inventory.use(targeting_item, entities=entities, fov_map=fov_map, target_x=player_target.x, target_y=player_target.y) player_turn_results.extend(item_use_results) for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_turn_result.get('dead') destroyed_entity = player_turn_result.get('destroyed') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') item_dropped = player_turn_result.get('item_dropped') targeting = player_turn_result.get('targeting') targeting_cancelled = player_turn_result.get('targeting_cancelled') if message: message_log.add_message(message) if targeting_cancelled: game_state = previous_game_state message_log.add_message(Message('Targeting cancelled')) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) message_log.add_message(message) if destroyed_entity: message = destroy_object(destroyed_entity) message_log.add_message(message) if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_consumed: game_state = GameStates.ENEMY_TURN if targeting: previous_game_state = GameStates.PLAYER_TURN game_state = GameStates.TARGETING player_target.set(player.x, player.y) targeting_item = targeting message_log.add_message(targeting_item.item.targeting_message) if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if game_state == GameStates.ENEMY_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn(player, fov_map, current_map, entities) for enemy_turn_result in enemy_turn_results: message = enemy_turn_result.get('message') dead_entity = enemy_turn_result.get('dead') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) message_log.add_message(message) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYER_TURN if game_state == GameStates.LOOKING and move_target: dx, dy = move_target player_target.move(dx, dy)