Search not working for river crossing problem in python - python-3.x

The problem of the river crossing description:
We have a farmer, a wolf, a goat and a cabbage and they need to cross the river with the following restrictions:
The wolf can’t be on the shame side with the goat
The goat can’t stay at the same side with the cabbage
-Initial state: (‘L’,‘L’,‘L’,‘L’)
-Finale state: (‘R’,‘R’,‘R’,‘R’)
The state list describes where everyone is, (farmer, wolf, goat, cabbage) so state(‘L’,‘R’,‘L’,‘R’) means that the wolf and the cabbage are on the right side and the farmer and the goat are on the left side of the river. I don’t want to make it more complex by implementing list of lists.
The problem with my code is that it starts with the initial state and after the first step (farmer and goat on the right) it stops. I can’t find any way to make the recursion to work in order to have an acceptable result. So I could use some help in order to make the recursion functional and work as intended.
Thank you in advance.
""" ----------------------------------------------------------------------------
******** Search Code for DFS and other search methods
******** (expanding front and extending queue)
******** author:
"""
import copy
#left, right of the river
spaces = {
'R': ['L'],
'L': ['R']
}
def enter(state):
if state[0]==state[1] and state[2]==state[3]:
new_state=state=['R', state[1]] + ['R', state[3]]
return new_state
#function that swaps the side of the river that the farmer is
def swap(state_l, i, j):
state_l[i] = state_l[j]
return state_l
'''
operators for the movement of the farmer
'''
def neighbour1(state):
elem=['L']
i=state.index(elem) if elem in state else -1 #checking if the elem is in the state
if i >=0:
swap(state, i, spaces[i][0])
return state
def neighbour2(state):
elem=['L']
i=state.index(elem) if elem in state else -1
if i >=0:
swap(state, i, spaces[i][1])
return state
""" ----------------------------------------------------------------------------
**** FRONT managment
****
"""
def find_children(state):
children=[]
enter_state=copy.deepcopy(state)
enter_child=enter(enter_state)
tr1_state=copy.deepcopy(state)
tr1_child=neighbour1(tr1_state)
tr2_state=copy.deepcopy(state)
tr2_child=neighbour2(tr2_state)
if tr1_child is not None:
children.append(tr1_child)
if tr2_child is not None:
children.append(tr2_child)
if enter_child is not None:
children.append(enter_child)
return children
""" ----------------------------------------------------------------------------
** initialization of front
**
"""
def make_front(state):
return [state]
""" ----------------------------------------------------------------------------
**** expanding front
****
"""
def expand_front(front, method):
if method=='DFS':
if front:
print("Front:")
print(front)
node=front.pop(0)
for child in find_children(node):
front.insert(0,child)
elif method=='BFS':
if front:
print("Front:")
print(front)
node=front.pop(0)
for child in find_children(node):
front.append(child)
return front
'''
elif method=='BestFS':
#'''
#else: "other methods to be added"
""" ----------------------------------------------------------------------------
**** QUEUE
****
"""
""" ----------------------------------------------------------------------------
** initialization of queue
**
"""
def make_queue(state):
return [[state]]
""" ----------------------------------------------------------------------------
**** expanding queue
**** επέκταση ουράς
"""
def extend_queue(queue, method):
if method=='DFS':
print("Queue:")
print(queue)
node=queue.pop(0)
queue_copy=copy.deepcopy(queue)
children=find_children(node[-1])
for child in children:
path=copy.deepcopy(node)
path.append(child)
queue_copy.insert(0,path)
elif method=='BFS':
if queue:
print("Queue:")
print(queue)
node=queue.pop(0)
queue_copy=copy.deepcopy(queue)
children=find_children(node[-1])
for child in children:
path=copy.deepcopy(node)
path.append(child)
queue_copy.append(path)
'''
elif method=='BestFS':
#'''
#else: "other methods to be added"
return queue_copy
""" ----------------------------------------------------------------------------
**** Basic recursive function to create search tree (recursive tree expansion)
****
"""
def find_solution(front, queue, closed, method):
if not front:
print('_NO_SOLUTION_FOUND_')
elif front[0] in closed:
new_front=copy.deepcopy(front)
new_front.pop(0)
new_queue=copy.deepcopy(queue)
new_queue.pop(0)
find_solution(new_front, new_queue, closed, method)
elif is_goal_state(front[0]):
print('_GOAL_FOUND_')
print(queue[0])
else:
closed.append(front[0])
front_copy=copy.deepcopy(front)
front_children=expand_front(front_copy, method)
queue_copy=copy.deepcopy(queue)
queue_children=extend_queue(queue_copy, method)
closed_copy=copy.deepcopy(closed)
find_solution(front_children, queue_children, closed_copy, method)
"""" ----------------------------------------------------------------------------
** Executing the code
**
"""
def is_goal_state(state):
if state == ['R','R','R','R']:
return True
def main():
initial_state = ['L','L','L','L']
method='DFS'
""" ----------------------------------------------------------------------------
**** starting search
****
"""
print('____BEGIN__SEARCHING____')
find_solution(make_front(initial_state), make_queue(initial_state), [], method)
if __name__ == "__main__":
main()

The Farmer Problem
I'm going to approach your problem from a different angle. Instead of debugging your problem I've solved it in a step-by-step method that has tests as we go.
The setup and rules
First lets define the situation and the rules
FARMER = 0
WOLF = 1
GOAT = 2
CABBAGE = 3
START_STATE = ['L','L','L','L']
def wolfRule(state):
return state[FARMER] == state[WOLF] or state[WOLF] != state[GOAT]
assert( wolfRule(['L','L','L','L']) == True)
assert( wolfRule(['R','L','L','L']) == False)
assert( wolfRule(['R','L','R','L']) == True)
def goatRule(state):
return state[FARMER] == state[GOAT] or state[GOAT] != state[CABBAGE]
assert( goatRule(['L','L','L','L']) == True)
assert( goatRule(['R','L','L','L']) == False)
assert( goatRule(['R','L','L','R']) == True)
def validRule(state):
return wolfRule(state) and goatRule(state)
def winRule(state):
return state == ['R','R','R','R']
assert( winRule(['L','L','L','L']) == False)
assert( winRule(['R','L','L','L']) == False)
assert( winRule(['R','R','R','R']) == True)
We have defined each rule, added some assert statements so we know they work, and when we run the above code we can be sure it all is good-to-go.
Generating moves
Part of the recursive search is to generate the next valid move. We will do this in two parts. The first just generates all possible moves, and the second part will filter out only the valid moves
def generateMoves(state):
# The farmer moves to the other side and can bring 0 or 1 other things so long as it is on the same starting side
for other in [FARMER, WOLF, GOAT, CABBAGE]:
if state[FARMER] == state[other]:
move = copy(state)
move[FARMER] = 'L' if state[FARMER] == 'R' else 'R'
move[other] = 'L' if state[other] == 'R' else 'R'
yield move
assert( list(generateMoves(START_STATE)) == [['R','L','L','L'],['R','R','L','L'],['R','L','R','L'],['R','L','L','R']] )
Again, we add a test to make sure it is doing what we expect when we generate some moves
def validMoves(state_list):
return [ state for state in state_list if validRule(state)]
assert( list(validMoves(generateMoves(START_STATE))) == [['R','L','R','L']] )
Indeed the only first valid move is for the farmer to move the goat!
Depth First Search
Now we do a depth first search, using a previous_states list to keep track of where we have been. This function only returns a winning answer.
def depthFirstSearch(state, previous_states):
previous_states.append(state)
if winRule(state):
return previous_states
for move in validMoves(generateMoves(state)):
if move not in previous_states:
result = depthFirstSearch(move, previous_states)
if result is not None:
return result
previous_states.pop()
return None
again, at least one test to make sure it is giving expected results
assert( depthFirstSearch(['L','R','L','R'],[]) == [['L','R','L','R'],['R','R','R','R']] )
Final output
We run it
depthFirstSearch(START_STATE,[])
and get the solution!
[['L', 'L', 'L', 'L'],
['R', 'L', 'R', 'L'],
['L', 'L', 'R', 'L'],
['R', 'R', 'R', 'L'],
['L', 'R', 'L', 'L'],
['R', 'R', 'L', 'R'],
['L', 'R', 'L', 'R'],
['R', 'R', 'R', 'R']]

Related

Data Structure Option

I'm wondering what appropriate data structure I'm going to use to store information about chemical elements that I have in a text file. My program should
read and process input from the user. If the user enters an integer then it program
should display the symbol and name of the element with the number of protons
entered. If the user enters a string then my program should display the number
of protons for the element with that name or symbol.
The text file is formatted as below
# element.txt
1,H,Hydrogen
2,He,Helium
3,Li,Lithium
4,Be,Beryllium
...
I thought of dictionary but figured that mapping a string to a list can be tricky as my program would respond based on whether the user provides an integer or a string.
You shouldn't be worried about the "performance" of looking for an element:
There are no more than 200 elements, which is a small number for a computer;
Since the program interacts with a human user, the human will be orders of magnitude slower than the computer anyway.
Option 1: pandas.DataFrame
Hence I suggest a simple pandas DataFrame:
import pandas as pd
df = pd.read_csv('element.txt')
df.columns = ['Number', 'Symbol', 'Name']
def get_column_and_key(s):
s = s.strip()
try:
k = int(s)
return 'Number', k
except ValueError:
if len(s) <= 2:
return 'Symbol', s
else:
return 'Name', s
def find_element(s):
column, key = get_column_and_key(s)
return df[df[column] == key]
def play():
keep_going = True
while keep_going:
s = input('>>>> ')
if s[0] == 'q':
keep_going = False
else:
print(find_element(s))
if __name__ == '__main__':
play()
See also:
Finding elements in a pandas dataframe
Option 2: three redundant dicts
One of python's most used data structures is dict. Here we have three different possible keys, so we'll use three dict.
import csv
with open('element.txt', 'r') as f:
data = csv.reader(f)
elements_by_num = {}
elements_by_symbol = {}
elements_by_name = {}
for row in data:
num, symbol, name = int(row[0]), row[1], row[2]
elements_by_num[num] = num, symbol, name
elements_by_symbol[symbol] = num, symbol, name
elements_by_name[name] = num, symbol, name
def get_dict_and_key(s):
s = s.strip()
try:
k = int(s)
return elements_by_num, k
except ValueError:
if len(s) <= 2:
return elements_by_symbol, s
else:
return elements_by_name, s
def find_element(s):
d, key = get_dict_and_key(s)
return d[key]
def play():
keep_going = True
while keep_going:
s = input('>>>> ')
if s[0] == 'q':
keep_going = False
else:
print(find_element(s))
if __name__ == '__main__':
play()
You are right that it is tricky. However, I suggest you just make three dictionaries. You certainly can just store the data in a 2d list, but that'd be way harder to make and access than using three dicts. If you desire, you can join the three dicts into one. I personally wouldn't, but the final choice is always up to you.
weight = {1: ("H", "Hydrogen"), 2: ...}
symbol = {"H": (1, "Hydrogen"), "He": ...}
name = {"Hydrogen": (1, "H"), "Helium": ...}
If you want to get into databases and some QLs, I suggest looking into sqlite3. It's a classic, thus it's well documented.

Program doesn't import coordinates like it should

I am a student.
This program i have been working on it for 2 weeks i need help to run it.
It doesn't import coordinates like it should.
I have posted coordinates on my previous question.
After getting the path it simply displays:
enter at least 3 sides
then closes
I am supposed to
import coordinates which are (x,y) at least 3.
Compute area
print the massage displaying area and polygon type.
def getcoord():
'''this function read coordinates from a csv file'''
myfile= input('Enter the path of file : ')
filename = 'CoordsT.txt'
with open('filename', 'r') as csvfile:
reader = csv.reader(csvfile)
next(reader)
return list(reader)
data=[]
for row in reader:
Name=row[0]
E=float(row[1])
N=float(row[2])
data.append([Name,E,N])
return data
def areacalculation(data): #function that calculate the area of polygon,returns area and no of side
n=len(data)
semiarea= 0.0
j = n - 1
if n>=3:
for i in range(0,n):
semiarea += (float(data[j][1] + data[i][1]) * (data[j][2]- data[i][2]))
j = i #j is previous vertex to i
return float(abs(semiarea / 2.0))
else:
print('Enter at least 3 sides of polygon')
def No_ofsides(polytyp):
polygontype = No_ofsides
return No_ofsides
def polygontype(n): #function that gives the type of polygon, takes argument
my_dict={3:'Triangle', 4:'Quadrilateral', 5:'Pentagon', 6:'Hexagon', 7:'Heptagon', 8:'Octagon', 9:'Nonagon', 10:'Decagon'}
return my_dict.items()
def square_metre(area): #function with argument
A2=area
return (A2)
def hectares(area): # fuction with argument
A1=area
return ("% .4f".format(A1))
def main():
instruction()
data = getcoord()
a =areacalculation(data)
hectares(a)
sqm=square_metre(a)
polytyp=polygontype(No_ofsides)
#area_hec=a/10000.00
if a<=10000.00:
print('The area of: {}, is, {}.'.format('polytyp, hectares'))
else:
print('The area of: {}, is, {}.'.format('polytyp, square_metre'))
main()
One of the big things in assignments like this is to get a framework that is working and go from there. I tweaked only a few parts of your code and put in some print statements. Try this out as a start...
Getting the area from a list of points can be a challenge unless you have some guarantees about the order of the points received. Perhaps your assignment has some additional information you can use.
This will at least get things going as it runs without errors and reads the file and exercises your functions.
import csv
def getcoord():
'''this function read coordinates from a csv file'''
# myfile= input('Enter the path of file : ') # let's start by hard-coding this for testing
filename = 'points.csv'
with open(filename, 'r') as csvfile: # filename here is a variable, not a string
reader = csv.reader(csvfile)
# next(reader)
# return list(reader) # what is this?
data=[]
for row in reader: # Good! this is the way to go!
Name=row[0]
E=float(row[1])
N=float(row[2])
data.append([Name,E,N])
return data
def areacalculation(data): #function that calculate the area of polygon,returns area and no of side
return -1 # this is a dummy value just to get your code running
n=len(data)
semiarea= 0.0
j = n - 1
if n>=3:
for i in range(0,n):
semiarea += (float(data[j][1] + data[i][1]) * (data[j][2]- data[i][2]))
j = i #j is previous vertex to i
return float(abs(semiarea / 2.0))
else:
print('Enter at least 3 sides of polygon')
# def No_ofsides(polytyp):
# polygontype = No_ofsides
# return No_ofsides
def polygontype(data): #function that gives the type of polygon, takes argument
num_sides = len(data)
my_dict={3:'Triangle', 4:'Quadrilateral', 5:'Pentagon', 6:'Hexagon', 7:'Heptagon', 8:'Octagon', 9:'Nonagon', 10:'Decagon'}
return my_dict.get(num_sides)
def square_metre(area): #function with argument
A2=area
return (A2)
def hectares(area): # fuction with argument
A1=area
return ("% .4f".format(A1))
def main():
# instruction()
data = getcoord()
# let's do a little check here.... we can comment this out later
print('data read: ', data)
a = areacalculation(data)
print('area calculated: ', a)
# hectares(a)
# sqm=square_metre(a)
polytyp=polygontype(data) # just send this function your data and let it do the work!
print('polygon type described: ', polytyp)
#area_hec=a/10000.00
# if a<=10000.00:
# print('The area of: {}, is, {}.'.format('polytyp, hectares'))
# else:
# print('The area of: {}, is, {}.'.format('polytyp, square_metre'))
main()
Output:
data read: [['A', 10.0, 10.0], ['B', 10.0, 20.0], ['C', 20.0, 20.0], ['D', 20.0, 10.0]]
area calculated: -1
polygon type described: Quadrilateral
datafile I used for this:
A, 10, 10
B, 10, 20
C, 20, 20
D, 20, 10

turn lisp command to nested lists in python

I am building a lisp parser in python 3.7.
Imagine I have this list program as a string
"(begin (define r 10) (* pi (* r r)))"
which I tokenize using:
def tokenize(string):
return string.replace('(', ' ( ').replace(')', ' ) ').split()
returning
['(', 'begin', '(', 'define', 'r', '10', ')', '(', '*', 'pi', '(', '*', 'r', 'r', ')', ')', ')']
Now I am trying to build a function that reads from this list of tokens and returns this.
['begin', ['define', 'r', '10'], ['*', 'pi', ['*', 'r', 'r']]]
Any idea is welcome.
Here is (at last!) the programme i created using recursion.
class LisParser:
"""class expecting a lisp program to recursively walk through."""
def __init__(self):
self.program = input(("Please provide lisp programme "
"you want to pythonize"
"(no need to pass the program as a string) : "))
self.sub_program_sep = "("
self.program_stack = self._tokenize_program()
self.atom_is_int = self._is_int
self.atom_is_flt = self._is_flt
self.recursive_unpack = self.recursive_unpack
def _tokenize_program(self):
"""func that splits a lisp program on white spaces ensuring parentheses tokenisation."""
# if accidental multiple spaces, join method collapses them before padding parentheses before and after.
tokens = " ".join(self.program.split()).replace('(', ' ( ').replace(')', ' ) ')
# user might have inputted lisp program as python string i.e. "program". If so, get rid of double quotes.
return (self.program.startswith('"')
and tokens.split()[1:-1]
or tokens.split())
#staticmethod
def _walk_stack(stack):
"""func returning the popped element at index 0 of the stack."""
return stack.pop(0)
#staticmethod
def _is_flt(atom):
"""func trying to turn an atom to an float, else throws error."""
try:
float(atom)
return True
except ValueError:
return False
#staticmethod
def _is_int(atom):
"""func trying to turn an atom to an int, else throws error."""
try:
int(atom)
return True
except ValueError:
return False
def _to_py_type(self, atom):
"""func that trying to an atom to an int, then a float and finally a string."""
return ((self.atom_is_int(atom) and int(atom)) or
(self.atom_is_flt(atom) and float(atom)) or
str(atom))
def recursive_unpack(self):
# _walk_stack pops the first element off the stack.
stack_head = self._walk_stack(stack=self.program_stack)
# if token is an atom, convert to python type.
if stack_head != self.sub_program_sep:
return self._to_py_type(atom=stack_head)
# "(" starts a sub_program, needs to be in its own unit (list).
# The nested lists will represent the ast of the lisp program.
elif stack_head == self.sub_program_sep:
ast = list()
# recursion base case is the end of the sub_program with ")".
while self.program_stack[0] != ")":
ast.append(self.recursive_unpack())
else:
# remove the closing parent, so that walk_atom() will return the atom, not the closing paren.
self.program_stack.remove(")")
return ast
if __name__ == '__main__':
parser = LisParser()
# when prompted, type "(first (list 1 (+ 2 3) 9))"
var = parser.recursive_unpack()
print(var)
# ['first', ['list', 1, ['+', 2, 3], 9]]

Disjoint set implementation in Python

I am relatively new to Python. I am studying Disjoint sets, and implemented it as follows:
class DisjointSet:
def __init__(self, vertices, parent):
self.vertices = vertices
self.parent = parent
def find(self, item):
if self.parent[item] == item:
return item
else:
return self.find(self.parent[item])
def union(self, set1, set2):
self.parent[set1] = set2
Now in the driver code:
def main():
vertices = ['a', 'b', 'c', 'd', 'e', 'h', 'i']
parent = {}
for v in vertices:
parent[v] = v
ds = DisjointSet(vertices, parent)
print("Print all vertices in genesis: ")
ds.union('b', 'd')
ds.union('h', 'b')
print(ds.find('h')) # prints d (OK)
ds.union('h', 'i')
print(ds.find('i')) # prints i (expecting d)
main()
So, at first I initialized all nodes as individual disjoint sets. Then unioned bd and hb which makes the set: hbd then hi is unioned, which should (as I assumed) give us the set: ihbd. I understand that due to setting the parent in this line of union(set1, set2):
self.parent[set1] = set2
I am setting the parent of h as i and thus removing it from the set of bd. How can I achieve a set of ihbd where the order of the params in union() won't yield different results?
Your program is not working correctly because you have misunderstood the algorithm for disjoint set implementation. Union is implemented by modifying the parent of the root node rather than the node provided as input. As you have already noticed, blindly modifying parents of any node you receive in input will just destroy previous unions.
Here's a correct implementation:
def union(self, set1, set2):
root1 = self.find(set1)
root2 = self.find(set2)
self.parent[root1] = root2
I would also suggest reading Disjoint-set data structure for more info as well as possible optimizations.
To make your implementation faster, you may want to update the parent as you find()
def find(self, item):
if self.parent[item] == item:
return item
else:
res = self.find(self.parent[item])
self.parent[item] = res
return res

accessing a static dict in Python 3.6

I have an Enum class of compass directions as follows.
I also have an 'opposites' dict declared in the same class.
from enum import Enum
class Compass(Enum):
N = 'N' # North
S = 'S' # South
E = 'E' # East
W = 'W' # West
opposites = {N: S, S: N, E: W, W: E}
# static method to provide the opposite values.
#staticmethod
def other(com):
return opposites[com]
when I attempt to call other, eg. Compass.other(Compass.N), I expect to get Compass.S, but instead I am getting..
TypeError: 'Com' object is not subscriptable
What's going on, and how can I remedy this pythonically ?
The basic problem is that opposite is being transformed into an Enum member just like N, S,E, and W are. The next problem is the values in opposite -- they do not get transformed into Enum members.
Ideally, we would have something like:
# NB: does not currently work!
class Compass(Enum):
N = 'N', S
S = 'S', N
E = 'E', W
W = 'W', E
Compass.E.opposite is Compass.W # True
The reasons this does not currently work are twofold:
the final transformation from plain value to Enum member happens after the class has been created
forward references are not allowed
So, to get a clean(er) implementation and API we have to post-process the Enum. I would use a decorator:
class reverse():
"decorator to add reverse lookups to Enum members"
def __init__(self, reverse_map):
"initialize decorator by saving map"
self.reverse_map = reverse_map
def __call__(self, enum):
"apply map to newly created Enum"
for first, second in self.reverse_map.items():
enum[first].opposite = enum[second]
enum[second].opposite = enum[first]
# don't forget to return the now-decorated Enum
return enum
and in use:
#reverse({'N':'S', 'E':'W'})
class Compass(Enum):
N = 'N' # North
S = 'S' # South
E = 'E' # East
W = 'W' # West
>>> Compass.N.opposite is Compass.S
True
Your custom class Compass is derived from Enum class which is enumeration but not subscriptable sequence.
Consider this line:
print(type(Compass.N))
While you expect it to output <class 'str'> - it outputs:
<enum 'Compass'>
To access enumaration object property use value attribute.
print(Compass.N.value) # prints "N"
print(Compass.opposites.value) # prints {'S': 'N', 'N': 'S', 'E': 'W', 'W': 'E'}
A proper Compass.other() function declaration should look as below:
# static method to provide the opposite values.
#staticmethod
def other(item):
if item in Compass.opposites.value:
return Compass.opposites.value[item]
else:
raise AttributeError('unknown compass direction :', item)
Usage:
print(Compass.other(Compass.N.value)) # prints "S"
#RomanPerekhrest got the credit for this purely due to speed of response, but it took a bit more wrangling to get what I wanted, which was an enum from the class. The cast to the Enum itself raises an error if bad input is put into it..
The class file folloeinh RomanPerekhrest that worked for me looks like this.
from enum import Enum
class Compass(Enum):
N = 'N' # North
S = 'S' # South
E = 'E' # East
W = 'W' # West
_opposites = {N: S, S: N, E: W, W: E}
#staticmethod
def other(item):
return Compass(Compass._opposites.value[item.value])
if __name__ == "__main__":
print(Compass.other(Compass.E))
However, #EthanFurman's response is beautiful, and I actually implemented that, not that I completely understand it yet...

Resources