Great Deal! Get Instant $10 FREE in Account on First Order + 10% Cashback on Every Order Order Now

 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 »
 README.md
Sokoban Environment - NYU CS-GY6613
Prerequisites
Requires python3 to run
Install li
aries
$ 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 playe
--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 XXXXXXXXXXor '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 cu
ent 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 cu
ent 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 cu
ent 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 cu
ent node (return type: str list)
getHeuristic() - returns the remaining heuristic cost for the cu
ent state - a.k.a. distance to win condition (return type: int)
getHash() - returns a unique hash for the cu
ent 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 cu
ent 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 e
or. 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 li
aries?
No. All the functions you need are already included in the code template. Do not import any internal or external li
aries for any
eason.
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_
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 Hillclimbe
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 Hillclimbe
and MCTS, around 70%-80% of levels should be solved.

screenshot XXXXXXXXXXat XXXXXXXXXXikkgusyk.png
screenshot XXXXXXXXXXat XXXXXXXXXXf2px315y.png
sokoban-y1ejkzaf.py
from PIL import Image
import os
#Cu
ent Sokoban State
class State:
#Empty Sokoban Level
def __init__(self):
XXXXXXXXXXself.solid=[]
XXXXXXXXXXself.targets=[]
XXXXXXXXXXself.crates=[]
XXXXXXXXXXself.player=None
#Initialize a Sokoban level from lines
def stringInitialize(self, lines):
XXXXXXXXXXself.solid=[]
XXXXXXXXXXself.targets=[]
XXXXXXXXXXself.crates=[]
XXXXXXXXXXself.player=None
# clean the input
XXXXXXXXXXfor i in range(len(lines)):
XXXXXXXXXXlines[i]=lines[i].replace("\n","")
XXXXXXXXXXfor i in range(len(lines)):
XXXXXXXXXXif len(lines[i].strip()) != 0:

eak
XXXXXXXXXXelse:
XXXXXXXXXXdel lines[i]
XXXXXXXXXXi-=1
XXXXXXXXXXfor i in range(len(lines)-1,0,-1):
XXXXXXXXXXif len(lines[i].strip()) != 0:

eak
XXXXXXXXXXelse:
XXXXXXXXXXdel lines[i]
XXXXXXXXXXi+=1
#get size of the map
XXXXXXXXXXself.width=0
XXXXXXXXXXself.height=len(lines)
XXXXXXXXXXfor l in lines:
XXXXXXXXXXif len(l) > self.width:
XXXXXXXXXXself.width = len(l)
#set the level
XXXXXXXXXXfor y in range(self.height):
XXXXXXXXXXl = lines[y]
XXXXXXXXXXself.solid.append([])
XXXXXXXXXXfor x in range(self.width):
XXXXXXXXXXif x > len(l)-1:
XXXXXXXXXXself.solid[y].append(False)
XXXXXXXXXXcontinue
XXXXXXXXXXc=l[x]
XXXXXXXXXXif c == "#":
XXXXXXXXXXself.solid[y].append(True)
XXXXXXXXXXelse:
XXXXXXXXXXself.solid[y].append(False)
XXXXXXXXXXif c == "@" or c=="+":
XXXXXXXXXXself.player={"x":x, "y":y}
XXXXXXXXXXif c=="$" or c=="*":
XXXXXXXXXXself.crates.append({"x":x, "y":y})
XXXXXXXXXXif c=="." or c=="+" or c=="*":
XXXXXXXXXXself.targets.append({"x":x, "y":y})
# Make a clone of the cu
ent state
def clone(self):
XXXXXXXXXXclone=State()
XXXXXXXXXXclone.width = self.width
XXXXXXXXXXclone.height = self.height
# since the solid is not changing then copy by value
XXXXXXXXXXclone.solid = self.solid
XXXXXXXXXXif hasattr(self, 'deadlocks'):
XXXXXXXXXXclone.deadlocks =
Answered Same Day Oct 01, 2021

Solution

Karthi answered on Oct 02 2021
142 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 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
[ 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
cu
entNode = queue.pop(0)
iterations += 1
# Check if already Visited
if cu
entNode.getHash() not in visited:
# Check if Winning State
if cu
entNode.checkWin():
bestNode = cu
entNode

eak
visited.add(cu
entNode.getHash())
queue.extend(cu
entNode.getChildren())
if bestNode is None or cu
entNode.getHeuristic() < bestNode.getHeuristic():
bestNode = cu
entNode
elif cu
entNode.getHeuristic() == bestNode.getHeuristic() and cu
entNode.getCost() < bestNode.getCost():
bestNode = cu
entNode
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
cu
entNode = stackDFS.pop()
# Check if already Visited
if cu
entNode.getHash() not in visited:
# Check if Winning State
if cu
entNode.checkWin():
bestNode = cu
entNode

eak
visited.add(cu
entNode.getHash())
stackDFS.extend(cu
entNode.getChildren())
if bestNode is None or cu
entNode.getHeuristic() < bestNode.getHeuristic():
bestNode = cu
entNode
elif cu
entNode.getHeuristic() == bestNode.getHeuristic() and cu
entNode.getCost() < bestNode.getCost():
bestNode = cu
entNode
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 ##
cu
entNode = queue.get()
# check if Node is not already visited
if cu
entNode.getHash() not in visited:
# check if Node is in goal state, then set node as bestNode and
eak while loop
if cu
entNode.state.checkWin():
bestNode = cu
entNode

eak
# if not goal state continue and add node to set of visited node
visited.add(cu
entNode.getHash())
# extract the children of the node and put in an a
ay
nodeChildren = []
nodeChildren.extend(cu
entNode.getChildren())
# insert the children nodes into the priority queue
for child in nodeChildren:
queue.put(child)
# update bestNode if the Heuristics of cu
Node is better,
eak tie with Cost
if bestNode is None or cu
entNode.getHeuristic() < bestNode.getHeuristic():
bestNode = cu
entNode
elif cu
entNode.getHeuristic() == bestNode.getHeuristic() and cu
entNode.getCost() < bestNode.getCost():
bestNode = cu
entNode
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
cu
Rank = 5
parentRouletteWheel = []
# allocate area in the roulette wheel based on fitness
for i in range(int(popSize/2)):
for j in range(cu
Rank):
parentRouletteWheel.append(i)
cu
Rank = cu
Rank-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