 README.md Sokoban Environment - NYU CS-GY6613 Prerequisites Requires python3 to run Install libraries $ pip install -r requirements.txt Run the Game Solve as a human $ python3 game.py --play $...

1 answer below »
Use python to code a Sokoban game using Breath first search, depth first search and A*


 README.md Sokoban Environment - NYU CS-GY6613 Prerequisites Requires python3 to run Install libraries $ pip install -r requirements.txt Run the Game Solve as a human $ python3 game.py --play $ python3 game.py --agent Human Solve with an agent $ python3 game.py --agent [AGENT-NAME-HERE] $ python3 game.py --agent BFS #run game with BFS agent $ python3 game.py --agent AStar --no_render #run game with AStar agent without rendering Parameters --play - run the game as a human player --no_render - run the AI solver without showing the game screen --agent [NAME] - the type of agent to use [Human, DoNothing, Random, BFS, DFS, AStar, HillClimber, Genetic, MCTS] --level [#] - which level to test (0-99) or 'random' for a randomly selected level that an agent can solve in at most 2000 iterations. These levels can be found in the 'assets/gen_levels/' folder (default=0) --iterations [#] - how many iterations to allow the agent to search for (default=3000) --solve_speed [#] - how fast (in ms) to show each step of the solution being executed on the game screen Code Functions These are the only functions you need to concern yourselves with to complete the assignments.WARNING: DO NOT MODIFY THESE FUNCTIONS! Sokoban_py state.clone() - creates a full copy of the current state (for use in initializing Nodes or for feedforward simulation of states without modifying the original) Use with HillClimber to test sequences state.checkWin() - checks if the game has been won in this state (return type: bool) state.update(x,y) - updates the state with the given direction in the form x,y where x is the change in x axis position and y is the change in y axis position. Used to feed-forward a state. Use with HillClimber Agent to test sequences. Agent_py Agent() - base class for the Agents RandomAgent() - agent that returns list of 20 random directions DoNothingAgent() - agent that makes no movement for 20 steps BFSAgent() - agent that solves the level using Breadth First Search DFSAgent() - agent that solves the level using Depth First Search AStarAgent() - agent that solves the level using A* Search HillClimberAgent() - agent that solves the level using HillClimber Search algorithm GeneticAgent() - agent that solves the level using Genetic Search algorithm MCTSAgent() - agent that solves the level using Monte Carlo Tree Search algorithm Helper_py Other functions getHeuristic(state) - returns the remaining heuristic cost for the current state - a.k.a. distance to win condition (return type: int). Use with HillClimber Agent to compare states at the end of sequence simulations directions - list of all possible directions (x,y) the agent/player can take Use with HillClimber Agent to mutate sequences Node Class __init__(state, parent, action) - where state is the current layout of the game map, parent is the Node object preceding the state, and action is the dictionary XY direction used to reach the state (return type: Node object) checkWin() - returns if the game is in a win state where all of the goals are covered by crates (return type: bool) getActions() - returns the sequence of actions taken from the initial node to the current node (return type: str list) getHeuristic() - returns the remaining heuristic cost for the current state - a.k.a. distance to win condition (return type: int) getHash() - returns a unique hash for the current game state consisting of the positions of the player, goals, and crates made of a string of integers - for use of keeping track of visited states and comparing Nodes (return type: str) getChildren() - retrieves the next consecutive Nodes of the current state by expanding all possible directional actions (return type: Node list) getCost() - returns the depth of the node in the search tree (return type: int) MCTSNode Class (extension of Node() for use with the MCTSAgent only) __init__(state, parent, action, maxDist) - modified to include variable to keep track of number of times visited (self.n), variable to keep track of score (self.q), and variable to keep make score value larger as solution gets nearer (self.maxDist) getChildren(visited) - returns the node's children if already made - otherwise creates new children based on whether states have been visited yet and saves them for use later (self.children) calcEvalScore(state) - calculates the evaluation score for a state compared to the node by examining the heurstic value compared to the starting heuristic value (larger = better = higher score) - for use with the rollout and general MCTS algorithm functions FAQs What is iterations and max_iterations ? iterations keeps track of how many nodes you can expand upon in the tree, and max_iterations is the number of nodes you are allowed to search. In the case of evolutionary search, this is the number of times the solution is allowed to evolve. This variable is implemented to prevent infinite loops from occuring in case your algorithm has an error. Each level in the Sokoban framework should be able to be solved in 3000 iterations (the default number) or less for every AI solver. Can I use other libraries? No. All the functions you need are already included in the code template. Do not import any internal or external libraries for any reason. I get infinite loops / My agent is running in circles. / My agent keeps going back to the same place. Make sure you are using node_a.getHash() == node_b.getHash() to check the equivalency of 2 nodes and NOT node_a == node_b Is it ok if my agent's solution is less than 50 steps but returned a solution length of 50 anyways? Yes, as long as it reaches the win state of the level. My Hillclimber/MCTS Agent doesn't win the level, is this ok? That's fine, as long as the final solution gets somewhat close to the win state. Because of the stochastic nature of Hillclimber and MCTS, around 70%-80% of levels should be solved. screenshot-2021-10-01-at-170017-ikkgusyk.png screenshot-2021-10-01-at-170011-f2px315y.png sokoban-y1ejkzaf.py from PIL import Image import os #Current Sokoban State class State: #Empty Sokoban Level def __init__(self): self.solid=[] self.targets=[] self.crates=[] self.player=None #Initialize a Sokoban level from lines def stringInitialize(self, lines): self.solid=[] self.targets=[] self.crates=[] self.player=None # clean the input for i in range(len(lines)): lines[i]=lines[i].replace("\n","") for i in range(len(lines)): if len(lines[i].strip()) != 0: break else: del lines[i] i-=1 for i in range(len(lines)-1,0,-1): if len(lines[i].strip()) != 0: break else: del lines[i] i+=1 #get size of the map self.width=0 self.height=len(lines) for l in lines: if len(l) > self.width: self.width = len(l) #set the level for y in range(self.height): l = lines[y] self.solid.append([]) for x in range(self.width): if x > len(l)-1: self.solid[y].append(False) continue c=l[x] if c == "#": self.solid[y].append(True) else: self.solid[y].append(False) if c == "@" or c=="+": self.player={"x":x, "y":y} if c=="$" or c=="*": self.crates.append({"x":x, "y":y}) if c=="." or c=="+" or c=="*": self.targets.append({"x":x, "y":y}) # Make a clone of the current state def clone(self): clone=State() clone.width = self.width clone.height = self.height # since the solid is not changing then copy by value clone.solid = self.solid if hasattr(self, 'deadlocks'): clone.deadlocks =
Answered Same DayOct 01, 2021

Answer To:  README.md Sokoban Environment - NYU CS-GY6613 Prerequisites Requires python3 to run Install...

Karthi answered on Oct 02 2021
140 Votes
solution_92567/agent.py
#########################################
# #
# #
# == SOKOBAN STUDENT AGENT CODE == #
# #
# Written by: [YOUR FULL NAME] #
# #
# #
#########################################
# SOLVER CLASSES WHERE AGENT CODES GO
from helper import *
import random
import math
# Base class of agent (DO NOT TOUCH!)
class Agent:
def getSolution(self, state, maxIterations):
'''
EXAMPLE USE FOR TREE SEARCH AGENT:
#expand the tree until the iterations runs out or a solution sequence is found
while (iterations < maxIterations or maxIterations <= 0) and len(queue) > 0:
iterations += 1
[ POP NODE OFF OF QUEUE ]
[ EVALUATE NODE AS WIN STATE]
[ IF WIN STATE: BREAK AND RETURN NODE'S ACTION SEQUENCE]
[ GET NODE'S CHILDREN ]
[ ADD VALID CHILDREN TO QUEUE ]
[ SAVE CURRENT BEST NODE ]
'''
'''
EXAMPLE USE FOR EVOLUTION BASED AGENT:
#expand t
he tree until the iterations runs out or a solution sequence is found
while (iterations < maxIterations or maxIterations <= 0) and len(queue) > 0:
iterations += 1
[ MUTATE ]
[ EVALUATE ]
[ IF WIN STATE: BREAK AND RETURN ]
[ SAVE CURRENT BEST ]
'''
return [] # set of actions
##### EXAMPLE AGENTS #####
# Do Nothing Agent code - the laziest of the agents
class DoNothingAgent(Agent):
def getSolution(self, state, maxIterations):
if maxIterations == -1: # RIP your machine if you remove this block
return []
#make idle action set
nothActionSet = []
for i in range(20):
nothActionSet.append({"x":0,"y":0})
return nothActionSet
# Random Agent code - completes random actions
class RandomAgent(Agent):
def getSolution(self, state, maxIterations):
#make random action set
randActionSet = []
for i in range(20):
randActionSet.append(random.choice(directions))
return randActionSet
##### ASSIGNMENT 1 AGENTS #####
# BFS Agent code
class BFSAgent(Agent):
def getSolution(self, state, maxIterations=-1):
intializeDeadlocks(state)
iterations = 0
bestNode = None
queue = [Node(state.clone(), None, None)]
visited = set()
#expand the tree until the iterations runs out or a solution sequence is found
while (iterations < maxIterations or maxIterations <= 0) and len(queue) > 0:
# YOUR CODE HERE
currentNode = queue.pop(0)
iterations += 1
# Check if already Visited
if currentNode.getHash() not in visited:
# Check if Winning State
if currentNode.checkWin():
bestNode = currentNode
break
visited.add(currentNode.getHash())
queue.extend(currentNode.getChildren())
if bestNode is None or currentNode.getHeuristic() < bestNode.getHeuristic():
bestNode = currentNode
elif currentNode.getHeuristic() == bestNode.getHeuristic() and currentNode.getCost() < bestNode.getCost():
bestNode = currentNode
return bestNode.getActions() #uncomment me
# DFS Agent Code
class DFSAgent(Agent):
def getSolution(self, state, maxIterations=-1):
intializeDeadlocks(state)
iterations = 0
bestNode = None
stackDFS = [Node(state.clone(), None, None)]
visited = set()

#expand the tree until the iterations runs out or a solution sequence is found
while (iterations < maxIterations or maxIterations <= 0) and len(stackDFS) > 0:
iterations += 1
# YOUR CODE HERE
currentNode = stackDFS.pop()
# Check if already Visited
if currentNode.getHash() not in visited:
# Check if Winning State
if currentNode.checkWin():
bestNode = currentNode
break
visited.add(currentNode.getHash())
stackDFS.extend(currentNode.getChildren())
if bestNode is None or currentNode.getHeuristic() < bestNode.getHeuristic():
bestNode = currentNode
elif currentNode.getHeuristic() == bestNode.getHeuristic() and currentNode.getCost() < bestNode.getCost():
bestNode = currentNode
return bestNode.getActions() #uncomment me
# AStar Agent Code
class AStarAgent(Agent):
def getSolution(self, state, maxIterations=-1):
#setup
intializeDeadlocks(state)
iterations = 0
bestNode = None
#initialize priority queue
queue = PriorityQueue()
queue.put(Node(state.clone(), None, None))
visited = set()
while (iterations < maxIterations or maxIterations <= 0) and queue.qsize() > 0:
iterations += 1
## YOUR CODE HERE ##
currentNode = queue.get()
# check if Node is not already visited
if currentNode.getHash() not in visited:
# check if Node is in goal state, then set node as bestNode and break while loop
if currentNode.state.checkWin():
bestNode = currentNode
break
# if not goal state continue and add node to set of visited node
visited.add(currentNode.getHash())
# extract the children of the node and put in an array
nodeChildren = []
nodeChildren.extend(currentNode.getChildren())
# insert the children nodes into the priority queue
for child in nodeChildren:
queue.put(child)
# update bestNode if the Heuristics of currNode is better, break tie with Cost
if bestNode is None or currentNode.getHeuristic() < bestNode.getHeuristic():
bestNode = currentNode
elif currentNode.getHeuristic() == bestNode.getHeuristic() and currentNode.getCost() < bestNode.getCost():
bestNode = currentNode
return bestNode.getActions() #uncomment me
##### ASSIGNMENT 2 AGENTS #####
# Hill Climber Agent code
class HillClimberAgent(Agent):
def getSolution(self, state, maxIterations=-1):
#setup
intializeDeadlocks(state)
iterations = 0

seqLen = 50 # maximum length of the sequences generated
coinFlip = 0.5 # chance to mutate
#initialize the first sequence (random movements)
bestSeq = []
for i in range(seqLen):
bestSeq.append(random.choice(directions))
#mutate the best sequence until the iterations runs out or a solution sequence is found
while (iterations < maxIterations):
iterations += 1

## YOUR CODE HERE ##
# clone state as bestState and update bestState to reflect bestSeq
bestState = state.clone()
for direction in bestSeq:
bestState.update(direction['x'], direction['y'])
# check if bestState is goal state, else start mutation process
if bestState.checkWin():
return bestSeq
# mutate the bestSeq and store in mutatedSeq
mutatedSeq = []
for i in range(seqLen):
# coinFlip mutation
if random.random() < coinFlip:
mutatedSeq.append(random.choice(directions))
else:
mutatedSeq.append(bestSeq[i])
# clone state as mutatedState and apply the mutatedSeq to it
mutatedState = state.clone()
for direction in mutatedSeq:
mutatedState.update(direction['x'], direction['y'])
# check if mutated state is goal state else compare heuristic to update bestSeq
if mutatedState.checkWin():
return mutatedSeq
elif getHeuristic(mutatedState) < getHeuristic(bestState):
for i in range(seqLen):
bestSeq[i] = mutatedSeq[i]
#return the best sequence found
return bestSeq
# Genetic Algorithm code
class GeneticAgent(Agent):
def getSolution(self, state, maxIterations=-1):
#setup
intializeDeadlocks(state)
iterations = 0
seqLen = 50 # maximum length of the sequences generated
popSize = 10 # size of the population to sample from
parentRand = 0.5 # chance to select action from parent 1 (50/50)
mutRand = 0.3 # chance to mutate offspring action
bestSeq = [] #best sequence to use in case iterations max out
#initialize the population with sequences of POP_SIZE actions (random movements)
population = []
for p in range(popSize):
bestSeq = []
for i in range(seqLen):
bestSeq.append(random.choice(directions))
population.append(bestSeq)
#mutate until the iterations runs out or a solution sequence is found
while (iterations < maxIterations):
iterations += 1
#1. evaluate the population
evaluatedPopulation = []
for individual in population:
individualState = state.clone()
for direction in individual:
individualState.update(direction['x'], direction['y'])
if(individualState.checkWin()):
return individual
fitness = getHeuristic(individualState)
evaluatedPopulation.append((fitness, individual))

#2. sort the population by fitness (low to high)
evaluatedPopulation.sort(key=(lambda x: x[0]))
#2.1 save bestSeq from best evaluated sequence
bestSeq = []
for i in range(seqLen):
bestSeq.append(evaluatedPopulation[0][1][i])
#3. generate probabilities for parent selection based on fitness
currRank = 5
parentRouletteWheel = []
# allocate area in the roulette wheel based on fitness
for i in range(int(popSize/2)):
for j in range(currRank):
parentRouletteWheel.append(i)
currRank = currRank-1
#4. populate by crossover and mutation
new_pop = []
for i in range(int(popSize/2)):
#4.1 select 2 parents sequences based on probabilities generated
par1 = evaluatedPopulation[random.choice(
parentRouletteWheel)][1]
par2 = evaluatedPopulation[random.choice(
parentRouletteWheel)][1]

#4.2 make a child from the crossover of the two parent sequences
offspring = []
for seq in range(seqLen):
if(random.random() < parentRand):
offspring.append(par1[seq])
else:
offspring.append(par2[seq])
#4.3 mutate the child's actions
for seq in range(seqLen):
if(random.random() < mutRand):
offspring[seq] = random.choice(directions)
#4.4 add the child to the new population
new_pop.append(list(offspring))
#5. add top half from last population (mu + lambda)
for i in range(int(popSize/2)):
new_pop.append(evaluatedPopulation[i][1])
#6. replace the old population...
SOLUTION.PDF

Answer To This Question Is Available To Download

Related Questions & Answers

More Questions »

Submit New Assignment

Copy and Paste Your Assignment Here