Skip to content

Commit

Permalink
Pushing all work
Browse files Browse the repository at this point in the history
  • Loading branch information
BennyDubz committed Sep 27, 2023
1 parent 1d48ff4 commit 60a2998
Show file tree
Hide file tree
Showing 30 changed files with 3,195 additions and 0 deletions.
Binary file added .DS_Store
Binary file not shown.
153 changes: 153 additions & 0 deletions Maze.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
from time import sleep

# Maze.py
# original version by db, Fall 2017
# Feel free to modify as desired.

# Maze objects are for loading and displaying mazes, and doing collision checks.
# They are not a good object to use to represent the state of a robot mazeworld search
# problem, since the locations of the walls are fixed and not part of the state;
# you should do something else to represent the state. However, each Mazeworldproblem
# might make use of a (single) maze object, modifying it as needed
# in the process of checking for legal moves.

# Test code at the bottom of this file shows how to load in and display
# a few maze data files (e.g., "maze1.maz", which you should find in
# this directory.)

# the order in a tuple is (x, y) starting with zero at the bottom left

# Maze file format:
# # is a wall
# . is a floor
# the command \robot x y adds a robot at a location. The first robot added
# has index 0, and so forth.


class Maze:

# internal structure:
# self.walls: set of tuples with wall locations
# self.width: number of columns
# self.rows

def __init__(self, mazefilename):

self.robotloc = []
# read the maze file into a list of strings
f = open(mazefilename)
lines = []
for line in f:
line = line.strip()
# ignore blank lines
if len(line) == 0:
pass
elif line[0] == "\\":
#print("command")
# there's only one command, \robot, so assume it is that
parms = line.split()
x = int(parms[1])
y = int(parms[2])
self.robotloc.append(x)
self.robotloc.append(y)
else:
lines.append(line)
f.close()

# Adding this as the index to represent who's turn it is to move
self.robotloc.append(0)

self.width = len(lines[0])
self.height = len(lines)

self.map = list("".join(lines))

def index(self, x, y):
return (self.height - y - 1) * self.width + x

# returns True if the location is a floor
def is_floor(self, x, y):
if x < 0 or x >= self.width:
return False
if y < 0 or y >= self.height:
return False

return self.map[self.index(x, y)] == "."

def has_robot(self, x, y):
if x < 0 or x >= self.width:
return False
if y < 0 or y >= self.height:
return False

# Needed to add the -1 in order to account for the turn element in the state
for i in range(0, len(self.robotloc) - 1, 2):
rx = self.robotloc[i]
ry = self.robotloc[i + 1]
if rx == x and ry == y:
return True

return False


# function called only by __str__ that takes the map and the
# robot state, and generates a list of characters in order
# that they will need to be printed out in.
def create_render_list(self):
renderlist = list(self.map)

robot_number = 0

# Needed to add the -1 in order to account for the turn element in the state
for index in range(0, len(self.robotloc) - 1, 2):

x = self.robotloc[index]
y = self.robotloc[index + 1]

renderlist[self.index(x, y)] = robotchar(robot_number)
robot_number += 1

return renderlist

def __str__(self):

# render robot locations into the map
renderlist = self.create_render_list()

# use the renderlist to construct a string, by
# adding newlines appropriately

s = ""
for y in range(self.height - 1, -1, -1):
for x in range(self.width):
s += renderlist[self.index(x, y)]

s += "\n"

return s


def robotchar(robot_number):
return chr(ord("A") + robot_number)


# Some test code

if __name__ == "__main__":
test_maze1 = Maze("maze1.maz")
print(test_maze1)

#test_maze2 = Maze("maze2.maz")
#print(test_maze2)

test_maze3 = Maze("maze3.maz")
print(test_maze3)

print(test_maze3)
print(test_maze3.robotloc)

print(test_maze3.is_floor(2, 3))
print(test_maze3.is_floor(-1, 3))
print(test_maze3.is_floor(1, 0))

print(test_maze3.has_robot(1, 0))
90 changes: 90 additions & 0 deletions MazeworldProblem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from Maze import Maze
from time import sleep

# Author: Ben Williams '25
# Date: September 14th, 2023


class MazeworldProblem:

## you write the constructor, and whatever methods your astar function needs

def __init__(self, maze, goal_locations):
self.maze = maze
self.start_state = tuple(maze.robotloc)
self.goal_state = goal_locations
# Excludes final index that may represent whose turn it is
self.num_robots = len(maze.robotloc) // 2

def __str__(self):
string = "Mazeworld problem: "
return string

# given a sequence of states (including robot turn), modify the maze and print it out.
# (Be careful, this does modify the maze!)

def animate_path(self, path):
# reset the robot locations in the maze
self.maze.robotloc = tuple(self.start_state[0:(len(self.start_state) - 1)])
print("Start state: ", self.start_state)
print("Goal state: ", self.goal_state)
for state in path:
print(str(self))
self.maze.robotloc = tuple(state[0:(len(self.start_state) - 1)])
sleep(1)

print(str(self.maze))

# Move robots together or separately?
def get_successors(self, state):
# Update locations on the maze
self.maze.robotloc = list(state)
successor_states = []

# Determine which robot is being moved
robot_id = state[len(state) - 1]

# Go through all possible movements
for x_mov in range(-1, 2):
for y_mov in range(-1, 2):
# Ignore diagonal moves
if abs(x_mov) + abs(y_mov) > 1:
continue
new_state = []
new_state.extend(state)
new_state[robot_id * 2] += x_mov
new_state[1 + robot_id * 2] += y_mov
new_state[len(new_state) - 1] = (robot_id + 1) % self.num_robots
if self.is_free_space(new_state[robot_id * 2], new_state[1 + robot_id * 2]) or (x_mov == 0 and y_mov == 0):
successor_states.append(tuple(new_state))

return successor_states

# Returns whether a space is free and open on the current maze
def is_free_space(self, x, y):
if self.maze.is_floor(x, y) and not self.maze.has_robot(x, y):
return True
return False

# Returns whether all the robots are at the goal positions or not
def is_goal_state(self, state):
self.maze.robotloc = list(state)
# We ignore the turn-element index of the state
for i in range(len(state) - 1):
if state[i] != self.goal_state[i]:
return False
return True


# A bit of test code. You might want to add to it to verify that things
# work as expected.
if __name__ == "__main__":
test_maze3 = Maze("maze3.maz")
test_mp = MazeworldProblem(test_maze3, (1, 4, 1, 3, 1, 2))
print(test_mp.maze)
# print(test_mp.get_successors((1, 0, 1, 1, 2, 1, 0)))
# print(test_mp.get_successors((1, 0, 1, 1, 2, 1, 1)))
# print(test_mp.get_successors((1, 0, 1, 1, 2, 1, 2)))

print(test_mp.get_successors((1, 0, 3, 1, 2, 5, 0)))
# print(test_mp.is_free_space(1, 1))
27 changes: 27 additions & 0 deletions SearchSolution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class SearchSolution:
def __init__(self, problem, search_method):
self.problem_name = str(problem)
self.search_method = search_method
self.path = []
self.nodes_visited = 0
self.cost = 0

def __str__(self):
string = "----\n"
string += "{:s}\n"
string += "attempted with search method {:s}\n"

if len(self.path) > 0:

string += "number of nodes visited: {:d}\n"
string += "solution length: {:d}\n"
string += "cost: {:d}\n"
string += "path: {:s}\n"

string = string.format(self.problem_name, self.search_method,
self.nodes_visited, len(self.path), self.cost, str(self.path))
else:
string += "no solution found after visiting {:d} nodes\n"
string = string.format(self.problem_name, self.search_method, self.nodes_visited)

return string
100 changes: 100 additions & 0 deletions SensorlessProblem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from Maze import Maze
from time import sleep

# Author: Ben Williams '25
# Date: September 22nd, 2023


# A version of Maze world where the robot is blind and has no idea where it starts
class SensorlessProblem:
def __init__(self, maze):
self.maze = maze
# The robot could be anywhere - so we start with all the floor spaces
self.start_state = self.get_open_spaces()

# Goal state in this case is how many possibilities we want to have left
self.goal_state = 1

def get_successors(self, state):
successor_states = []

# Loop through each possible action, except this time we do not allow waiting
for x_mov in range(-1, 2):
for y_mov in range(-1, 2):
# Ignore diagonal moves
if abs(x_mov) + abs(y_mov) > 1:
continue

# Do not allow waiting
if x_mov == y_mov == 0:
continue

new_state = set()
# Try the movement on all current possible locations
for possible_location in state:
new_x = possible_location[0] + x_mov
new_y = possible_location[1] + y_mov

# If the floor is open from this possible location, we could end up there
if self.maze.is_floor(new_x, new_y):
new_state.add((new_x, new_y))
# Otherwise, we hit a wall and stay in the same spot
else:
new_state.add(possible_location)

successor_states.append(tuple(new_state))

return successor_states

def __str__(self):
string = "Blind robot problem: "
return string

# In this, the letters represent all possible locations the single robot could be in
# We animate the narrowing-down of positions over time
def animate_path(self, path):
# Convert the start state into a list of locations
start_list_of_tuples = list(self.start_state)
start_possible_positions = []
for possible_position in start_list_of_tuples:
start_possible_positions.extend(list(possible_position))

# Reset locations in the maze
self.maze.robotloc = start_possible_positions

for state in path:
print(str(self))

# Convert the state into a list of locations
list_of_tuples = list(state)
robot_possible_positions = []
for possible_position in list_of_tuples:
robot_possible_positions.extend(list(possible_position))
self.maze.robotloc = robot_possible_positions

sleep(1)
print(str(self.maze))

# Returns a list of all floor locations on the maze
def get_open_spaces(self):
open_spaces = []
# Find every floor location on the maze
for x in range(self.maze.width):
for y in range(self.maze.height):
if self.maze.is_floor(x, y):
open_spaces.append((x, y))
return tuple(open_spaces)

# If there is only one possible location, then we know where we are and the problem is solved
def is_goal_state(self, state):
return len(state) == 1

# So that we can run the BFS on this
def is_safe_state(self, state):
return True


## A bit of test code
if __name__ == "__main__":
test_maze3 = Maze("maze3.maz")
test_problem = SensorlessProblem(test_maze3)
Loading

0 comments on commit 60a2998

Please sign in to comment.