Compare commits
51 Commits
1d5e02949a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c2535274d | |||
| f03e390fa4 | |||
| 98a24a92cb | |||
| 470d4b0b2b | |||
| 34e88112c6 | |||
| 864d07348d | |||
| a30e9b2556 | |||
| 6949336bbe | |||
| 33e75d0529 | |||
| f0dda78b43 | |||
| e613278fa2 | |||
| 1b7352680b | |||
| deba677725 | |||
| 8dc989a0a8 | |||
| 5e3b122ab1 | |||
| 6c2d865d96 | |||
| 98a59532da | |||
| 2f6dd3858c | |||
| d4b6db8edf | |||
| 9ca023025b | |||
| 97bdfd1907 | |||
| ee4da2417d | |||
| 8a232759f6 | |||
| be479d2c63 | |||
| 73c6a283c6 | |||
| f566e71a48 | |||
| 05d5b66dec | |||
| d876bc9a73 | |||
| 9e8153eb1f | |||
| b2ecff6a6b | |||
| e25318b336 | |||
| bb366686c1 | |||
| 5b3f9eff6b | |||
| 511dd6a9db | |||
| 7c9090f292 | |||
| b86b6fac0e | |||
| 2b42ec7b12 | |||
| 9ccdd8effe | |||
| 966b9274e8 | |||
| 832ff2e4ea | |||
| 8f8bc4ff49 | |||
| 18100f133b | |||
| fa53f5b655 | |||
| 27b1145262 | |||
| 48d9b9be73 | |||
| edfb6fc61e | |||
| 860776789f | |||
| 7fbfc8fd03 | |||
| a866f5ea66 | |||
| f01b0069a0 | |||
| e4a69fd6d7 |
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -3,5 +3,5 @@
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.14 (test)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.14 (test)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.14" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
2
.idea/test.iml
generated
2
.idea/test.iml
generated
@@ -4,7 +4,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.14 (test)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.14" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"python-envs.defaultEnvManager": "ms-python.python:system"
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
######################################
|
||||
# DCS 229 -- Unfair Solitaire
|
||||
# This is the Card.py file, which defines the Card class used in the Solitaire game. The Card class represents a playing card with a suit and value, and includes methods to retrieve the suit and value, as well as a string representation of the card.
|
||||
# Date: 04/01/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: Fran
|
||||
##########################################
|
||||
class Card:
|
||||
"""
|
||||
Represents a playing card with a suit and value.
|
||||
@@ -20,3 +27,17 @@ class Card:
|
||||
return f"{self._value} of {self._suit}"
|
||||
|
||||
#Returns a string representation of the card in the format "Value of Suit".
|
||||
|
||||
if __name__ == "__main__":
|
||||
card1 = Card("Hearts", "Ace")
|
||||
card2 = Card("Spades", "10")
|
||||
|
||||
assert card1.get_suit() == "Hearts"
|
||||
assert card1.get_value() == "Ace"
|
||||
assert card2.get_suit() == "Spades"
|
||||
assert card2.get_value() == "10"
|
||||
|
||||
assert repr(card1) == "Ace of Hearts"
|
||||
assert repr(card2) == "10 of Spades"
|
||||
|
||||
print("All Card tests passed.")
|
||||
@@ -1,3 +1,11 @@
|
||||
######################################
|
||||
# DCS 229 -- Unfair Solitaire
|
||||
# This is the Deck.py file, which defines the Deck class used in the Solitaire game. The Deck class represents a standard 52-card deck, and includes methods to shuffle the deck, deal cards, and check the number of remaining cards.
|
||||
# Date: 04/01/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: Fran
|
||||
##########################################
|
||||
|
||||
import random
|
||||
from Card import Card
|
||||
|
||||
@@ -19,12 +27,18 @@ class Deck:
|
||||
self._cards.append(Card(suit, value)) #creates a standard 52-card deck
|
||||
|
||||
def shuffle(self):
|
||||
"""
|
||||
Shuffles the deck of cards using the Fisher-Yates algorithm. This algorithm iterates through the deck and swaps each card with a randomly selected card from the remaining unshuffled portion of the deck. After shuffling, it resets the next card index to 0.
|
||||
"""
|
||||
for i in range(len(self._cards)): #iterates through each card in the deck
|
||||
j = random.randrange(i, len(self._cards)) #selects a random index from i to the end of the deck
|
||||
self._cards[i], self._cards[j] = self._cards[j], self._cards[i] #Shuffles the deck
|
||||
self._next_card = 0 #Resets the next card index after shuffling.
|
||||
|
||||
def deal(self):
|
||||
"""
|
||||
Deals the next card from the deck. Returns None if there are no cards left to deal.
|
||||
"""
|
||||
if self._next_card >= len(self._cards):
|
||||
return None #Returns None if there are no cards left to deal.
|
||||
card = self._cards[self._next_card]
|
||||
@@ -34,6 +48,14 @@ class Deck:
|
||||
def number_of_cards(self) -> int:
|
||||
return len(self._cards) - self._next_card #Returns the number of remaining cards in the deck.
|
||||
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
Returns a string representation of the entire deck,
|
||||
with one card per line.
|
||||
"""
|
||||
return "\n".join(str(card) for card in self._cards)
|
||||
|
||||
if __name__ == "__main__":
|
||||
deck = Deck()
|
||||
deck.shuffle() #Calls the shuffle method to randomize the deck.
|
||||
|
||||
BIN
Project 1/Project 1.pdf
Normal file
BIN
Project 1/Project 1.pdf
Normal file
Binary file not shown.
@@ -1,19 +1,28 @@
|
||||
from Deck import Deck
|
||||
"""
|
||||
Defines the Solitaire game logic. Contains methods to play the game by dealing cards, removing cards based on game rules, and checking for a win condition.
|
||||
######################################
|
||||
# DCS 229 -- Unfair Solitaire
|
||||
# Defines the Solitaire game logic. Contains methods to play the game by dealing cards, removing cards based on game rules, and checking for a win condition.
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: Fran
|
||||
##########################################
|
||||
|
||||
from Deck import Deck
|
||||
|
||||
Important Note: The prints in this file only run when this file is ececuted directly. This file should not be executed directly normally.
|
||||
"""
|
||||
|
||||
class Solitaire:
|
||||
def __init__(self):
|
||||
self.deck = Deck()
|
||||
self.face_up = [] #List to hold face-up cards.
|
||||
|
||||
'''
|
||||
deals cards until there are four face-up cards or the deck is empty.
|
||||
'''
|
||||
def deal_until_four(self):
|
||||
while len(self.face_up) < 4 and self.deck.number_of_cards() > 0:
|
||||
self.face_up.append(self.deck.deal()) #Deals cards until there are four face-up cards.
|
||||
|
||||
'''
|
||||
removes the last four cards if they are all the same suit. Returns True if cards were removed, False otherwise.
|
||||
'''
|
||||
def remove_four_same_suit(self) -> bool:
|
||||
if len(self.face_up) < 4:
|
||||
return False #Not enough cards to remove four of the same suit.
|
||||
@@ -27,6 +36,9 @@ class Solitaire:
|
||||
|
||||
return False
|
||||
|
||||
'''
|
||||
removes the inner two cards if the first and last card of the last four cards are the same suit. Returns True if cards were removed, False otherwise.
|
||||
'''
|
||||
def remove_inner_two(self) -> bool:
|
||||
if len(self.face_up) < 4:
|
||||
return False #Not enough cards to remove inner two cards.
|
||||
@@ -38,6 +50,10 @@ class Solitaire:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
'''
|
||||
Attempts to remove cards based on game rules.
|
||||
'''
|
||||
def remove_all_possible(self):
|
||||
removed = True
|
||||
while removed and len(self.face_up) >= 4:
|
||||
@@ -45,6 +61,9 @@ class Solitaire:
|
||||
if not removed:
|
||||
removed = self.remove_inner_two() # Attempts to remove cards based on game rules.
|
||||
|
||||
'''
|
||||
plays the game by shuffling the deck, dealing cards, removing cards based on game rules, and checking for a win condition.
|
||||
'''
|
||||
def playGame(self) -> bool:
|
||||
self.deck.shuffle()
|
||||
self.face_up = []
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
"""
|
||||
This is the main file that should be called to run the simulation.
|
||||
"""
|
||||
######################################
|
||||
# DCS 229 -- Unfair Solitaire
|
||||
# This is the main.py file, which contains the main function to run the simulation.
|
||||
# Date: 04/01/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: Fran
|
||||
##########################################
|
||||
from Solitaire import Solitaire
|
||||
|
||||
|
||||
|
||||
2
Project 1/test.py
Normal file
2
Project 1/test.py
Normal file
@@ -0,0 +1,2 @@
|
||||
for i in range (0, 10):
|
||||
print(i)
|
||||
@@ -1,3 +1,11 @@
|
||||
######################################
|
||||
# DCS 229 -- Project 2
|
||||
# This is the Card.py file, which defines the Card class used in the Solitaire game. The Card class represents a playing card with a suit and value, and includes methods to retrieve the suit and value, as well as a string representation of the card.
|
||||
# Date: 04/01/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: Fran
|
||||
##########################################
|
||||
|
||||
class Card:
|
||||
"""
|
||||
Represents a playing card with a suit and value.
|
||||
@@ -23,9 +31,15 @@ class Card:
|
||||
|
||||
def __add__(self, other) -> int:
|
||||
#Adding 2 cards together
|
||||
|
||||
return "Cat"
|
||||
if isinstance(other, Card):
|
||||
return self.value + other.value
|
||||
return self._value + other._value
|
||||
elif isinstance(other, int):
|
||||
#allows adding a card to an int
|
||||
return self.value + other
|
||||
return self._value + other
|
||||
return NotImplemented
|
||||
|
||||
def __radd__(self, other) -> int:
|
||||
return self.__add__(other)
|
||||
#Added for project 2: Created an "__add__" meathod to allow adding Cards together.
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
######################################
|
||||
# DCS 229 -- Project 2
|
||||
# This is the Deck.py file, which defines the Deck class used in the Solitaire game. The Deck class represents a standard 52-card deck, and includes methods to shuffle the deck, deal cards, and check the number of remaining cards.
|
||||
# Date: 04/01/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: Fran
|
||||
##########################################
|
||||
|
||||
import random
|
||||
from Card import Card
|
||||
"""
|
||||
@@ -18,12 +26,18 @@ class Deck:
|
||||
self._cards.append(Card(suit, value)) #creates a standard 52-card deck
|
||||
|
||||
def shuffle(self):
|
||||
"""
|
||||
Shuffles the deck of cards using the Fisher-Yates algorithm. This algorithm iterates through the deck and swaps each card with a randomly selected card from the remaining unshuffled portion of the deck. After shuffling, it resets the next card index to 0.
|
||||
"""
|
||||
for i in range(len(self._cards)): #iterates through each card in the deck
|
||||
j = random.randrange(i, len(self._cards)) #selects a random index from i to the end of the deck
|
||||
self._cards[i], self._cards[j] = self._cards[j], self._cards[i] #Shuffles the deck
|
||||
self._next_card = 0 #Resets the next card index after shuffling.
|
||||
|
||||
def deal(self):
|
||||
"""
|
||||
Deals the next card from the deck. Returns None if there are no cards left to deal.
|
||||
"""
|
||||
if self._next_card >= len(self._cards):
|
||||
return None #Returns None if there are no cards left to deal.
|
||||
card = self._cards[self._next_card]
|
||||
@@ -33,6 +47,13 @@ class Deck:
|
||||
def number_of_cards(self) -> int:
|
||||
return len(self._cards) - self._next_card #Returns the number of remaining cards in the deck.
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
Returns a string representation of the entire deck,
|
||||
with one card per line.
|
||||
"""
|
||||
return "\n".join(str(card) for card in self._cards)
|
||||
|
||||
if __name__ == "__main__":
|
||||
deck = Deck()
|
||||
deck.shuffle() #Calls the shuffle method to randomize the deck.
|
||||
|
||||
BIN
Project 2/Project 2.pdf
Normal file
BIN
Project 2/Project 2.pdf
Normal file
Binary file not shown.
@@ -1,19 +1,28 @@
|
||||
from Deck import Deck
|
||||
"""
|
||||
Defines the Solitaire game logic. Contains methods to play the game by dealing cards, removing cards based on game rules, and checking for a win condition.
|
||||
######################################
|
||||
# DCS 229 -- Project 2
|
||||
# Defines the Solitaire game logic. Contains methods to play the game by dealing cards, removing cards based on game rules, and checking for a win condition.
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: Fran
|
||||
##########################################
|
||||
|
||||
from Deck import Deck
|
||||
|
||||
Important Note: The prints in this file only run when this file is ececuted directly. This file should not be executed directly normally.
|
||||
"""
|
||||
|
||||
class Solitaire:
|
||||
def __init__(self):
|
||||
self.deck = Deck()
|
||||
self.face_up = [] #List to hold face-up cards.
|
||||
|
||||
'''
|
||||
deals cards until there are four face-up cards or the deck is empty.
|
||||
'''
|
||||
def deal_until_four(self):
|
||||
while len(self.face_up) < 4 and self.deck.number_of_cards() > 0:
|
||||
self.face_up.append(self.deck.deal()) #Deals cards until there are four face-up cards.
|
||||
|
||||
'''
|
||||
removes the last four cards if they are all the same suit. Returns True if cards were removed, False otherwise.
|
||||
'''
|
||||
def remove_four_same_suit(self) -> bool:
|
||||
if len(self.face_up) < 4:
|
||||
return False #Not enough cards to remove four of the same suit.
|
||||
@@ -27,6 +36,9 @@ class Solitaire:
|
||||
|
||||
return False
|
||||
|
||||
'''
|
||||
removes the inner two cards if the first and last card of the last four cards are the same suit. Returns True if cards were removed, False otherwise.
|
||||
'''
|
||||
def remove_inner_two(self) -> bool:
|
||||
if len(self.face_up) < 4:
|
||||
return False #Not enough cards to remove inner two cards.
|
||||
@@ -38,6 +50,9 @@ class Solitaire:
|
||||
|
||||
return False
|
||||
|
||||
'''
|
||||
Attempts to remove cards based on game rules.
|
||||
'''
|
||||
def remove_all_possible(self):
|
||||
removed = True
|
||||
while removed and len(self.face_up) >= 4:
|
||||
@@ -45,6 +60,9 @@ class Solitaire:
|
||||
if not removed:
|
||||
removed = self.remove_inner_two() # Attempts to remove cards based on game rules.
|
||||
|
||||
'''
|
||||
plays the game by shuffling the deck, dealing cards, removing cards based on game rules, and checking for a win condition.
|
||||
'''
|
||||
def playGame(self) -> bool:
|
||||
self.deck.shuffle()
|
||||
self.face_up = []
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Project 2/__pycache__/main.cpython-314.pyc
Normal file
BIN
Project 2/__pycache__/main.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Project 2/__pycache__/utilities.cpython-314.pyc
Normal file
BIN
Project 2/__pycache__/utilities.cpython-314.pyc
Normal file
Binary file not shown.
@@ -1,6 +1,11 @@
|
||||
"""
|
||||
This is the main file that should be called to run the simulation.
|
||||
"""
|
||||
######################################
|
||||
# DCS 229 -- Unfair Solitaire
|
||||
# This is the main.py file, which contains the main function to run the simulation.
|
||||
# Date: 04/01/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: Fran
|
||||
##########################################
|
||||
|
||||
from Solitaire import Solitaire
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +1,37 @@
|
||||
"""
|
||||
Reo
|
||||
"""
|
||||
######################################
|
||||
# DCS 229 -- Project 2
|
||||
# This is the utilities.py file, which contains utility functions for the Solitaire game.
|
||||
# Date: 04/01/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: Fran
|
||||
##########################################
|
||||
|
||||
from __future__ import annotations
|
||||
from Card import Card
|
||||
|
||||
def sum_cards_iter(cards: list[Card]) -> int:
|
||||
'''
|
||||
Added for project 2: Created a function to sum a list of Card objects using iteration.
|
||||
'''
|
||||
def sum_cards_iter(cards: list[Card]) -> int: #start total at 0
|
||||
total = 0 #start total at 0
|
||||
for card in cards:
|
||||
total = total + card
|
||||
total = total + card #Adds the value of each card to the total using iteration.
|
||||
return total
|
||||
#Added for project 2: Created a function to sum a list of Card objects using iteration.
|
||||
|
||||
def sum_cards_recursive(cards: list[Card]) -> int:
|
||||
'''
|
||||
Added for project 2: Created a function to sum a list of Card objects using recursion.
|
||||
'''
|
||||
def sum_cards_recursive(cards: list[Card]) -> int: #start total at 0
|
||||
if cards == []:
|
||||
#Base case
|
||||
return 0
|
||||
return cards[0] + sum_cards_recursive(cards[1:])
|
||||
return 0 #If the list of cards is empty, return 0.
|
||||
return cards[0] + sum_cards_recursive(cards[1:]) #Adds the value of the first card to the sum of the remaining cards using recursion.
|
||||
#Recursive case
|
||||
#Added for project 2: Created a function to sum a list of Card objects using recursion.
|
||||
|
||||
|
||||
'''
|
||||
Added for project 2: Example tests given
|
||||
'''
|
||||
def test_sum_cards():
|
||||
cards = [
|
||||
Card("diamond", 5),
|
||||
@@ -26,6 +39,8 @@ def test_sum_cards():
|
||||
Card("spade", 12),
|
||||
Card("clubs", 1)
|
||||
]
|
||||
assert sum_cards_iter(cards) == 21
|
||||
assert sum_cards_recursive(cards) == 21
|
||||
#Added for project 2: Example tests given
|
||||
print(sum_cards_iter(cards))
|
||||
# assert sum_cards_iter(cards) == 21
|
||||
# assert sum_cards_recursive(cards) == 21
|
||||
|
||||
test_sum_cards()
|
||||
BIN
Project 3/OLD Project 3.pdf
Normal file
BIN
Project 3/OLD Project 3.pdf
Normal file
Binary file not shown.
BIN
Project 3/OLD V2 Project 3.pdf
Normal file
BIN
Project 3/OLD V2 Project 3.pdf
Normal file
Binary file not shown.
BIN
Project 3/__pycache__/project_3_search.cpython-314.pyc
Normal file
BIN
Project 3/__pycache__/project_3_search.cpython-314.pyc
Normal file
Binary file not shown.
@@ -6,46 +6,53 @@
|
||||
# https://pressbooks.palni.org/anopenguidetodatastructuresandalgorithms/chapter/search/
|
||||
#
|
||||
##########################################
|
||||
# import statement to support more type hints
|
||||
from __future__ import annotations
|
||||
|
||||
import random #to generate the 100 random values
|
||||
import time #for part 4, to calculae total time
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
#I decided to put all 4 functions into one class to keep it organized
|
||||
class Search:
|
||||
|
||||
"""
|
||||
This function generates the list of random integers.
|
||||
"""
|
||||
def generate_random_list(self, n):
|
||||
data = []
|
||||
for _ in range(n):
|
||||
data.append(random.randint(0, 1000))
|
||||
def generate_random_list(self, n: int) -> list[int]:
|
||||
"""
|
||||
This function generates a list of n random integers between 0 and 1000, and returns the list.
|
||||
Args: n: the number of random integers to generate
|
||||
Returns: a list of n random integers between 0 and 1000
|
||||
"""
|
||||
array: list[int] = [] #array starts as blank
|
||||
for element in range(n): #will run n times
|
||||
array.append(random.randint(0, 1000)) #random integer between 0-1000
|
||||
|
||||
if 42 not in data:
|
||||
data[random.randint(0, n - 1)] = 42
|
||||
#I added this for testing purposes, to make sure 42 was in the list
|
||||
#if 42 not in array:
|
||||
# array[random.randint(0, n - 1)] = 42
|
||||
|
||||
return data
|
||||
return array
|
||||
|
||||
"""
|
||||
This funciton preforms a search on the list for the target value, and prints the number of checks it took to find the target.
|
||||
"""
|
||||
def linear_search_print(self, arr, target):
|
||||
checks = 0
|
||||
def linear_search_print(self, arr: Sequence[int], target: int) -> int:
|
||||
"""
|
||||
This function performs a linear search for the target value in the given array. It prints the number of unsuccessful checks it took to find the target value, and returns the number of checks. If the target value is not found, it prints "Not found." and returns the total number of checks.
|
||||
Args: arr: the array to search through, target: the value to search for
|
||||
Returns: the number of checks it took to find the target value, or the total number of checks if the target value is not found
|
||||
"""
|
||||
checks = 0 #check count starts at 0
|
||||
for value in arr:
|
||||
if value == target:
|
||||
print("Unsuccessful checks:", checks)
|
||||
return checks
|
||||
checks += 1
|
||||
checks += 1 #adds 1 to the number of checks
|
||||
|
||||
print("Not found.")
|
||||
print("Not found.") #Only gets printed if the code doesnt return earlier
|
||||
return checks
|
||||
|
||||
"""
|
||||
This function pre
|
||||
"""
|
||||
def linear_search_count(self, arr, target):
|
||||
|
||||
def linear_search_count(self, arr: Sequence[int], target: int) -> int:
|
||||
"""
|
||||
This function performs a linear search for the target value in the given array. It returns the number of checks it took to find the target value, or the total number of checks if the target value is not found.
|
||||
Args: arr: the array to search through, target: the value to search for
|
||||
Returns: the number of checks it took to find the target value, or the total number of checks if the target value is not found
|
||||
"""
|
||||
checks = 0
|
||||
for value in arr:
|
||||
checks += 1
|
||||
@@ -54,23 +61,96 @@ class Search:
|
||||
return checks
|
||||
|
||||
|
||||
def binary_search_recursive(self, arr, target, low, high):
|
||||
def binary_search_recursive(
|
||||
self,
|
||||
arr: Sequence[int],
|
||||
target: int,
|
||||
low: int = 0,
|
||||
high: int | None = None,
|
||||
) -> int:
|
||||
"""
|
||||
This function performs a binary search for the target value in the given sorted array. It returns the index of the target value if found, or -1 if not found.
|
||||
Args: arr: the sorted array to search through, target: the value to search for
|
||||
Returns: the index of the target value if found, or -1 if not found
|
||||
"""
|
||||
if high is None:
|
||||
high = len(arr) - 1
|
||||
|
||||
if low > high:
|
||||
return -1
|
||||
return -1 #base case: if low is greater than high, the target is not found
|
||||
|
||||
mid = (low + high) // 2
|
||||
mid = (low + high) // 2 #finds the middle
|
||||
|
||||
if arr[mid] == target:
|
||||
return mid
|
||||
elif arr[mid] < target:
|
||||
if arr[mid] == target: #stops if target found
|
||||
return mid #in this case mid would be the index of the target
|
||||
elif arr[mid] < target: #either the left or right half of array is then searched
|
||||
return self.binary_search_recursive(arr, target, mid + 1, high)
|
||||
else:
|
||||
return self.binary_search_recursive(arr, target, low, mid - 1)
|
||||
|
||||
def main(n: int = 1000) -> None:
|
||||
"""
|
||||
This function is the main function that runs the search tests. It generates random lists of integers, performs linear and binary searches, and prints the results.
|
||||
Args: n: the number of random integers to generate for the tests (default is 1000)
|
||||
Returns: None
|
||||
"""
|
||||
search = Search() #search object to call Search() class
|
||||
|
||||
### write your tests in main function
|
||||
def main():
|
||||
pass
|
||||
"""
|
||||
Part 1: Linear search on 100 integers
|
||||
"""
|
||||
arr = search.generate_random_list(100) #generate the list of 100 random integers
|
||||
search.linear_search_print(arr, 42) #search for the value 42 using linear search and print the number of checks it took to find 42
|
||||
|
||||
"""
|
||||
Part 2: Same as 1, but return number of checks
|
||||
"""
|
||||
total = 0
|
||||
tests = 100
|
||||
|
||||
for _ in range(tests):
|
||||
arr = search.generate_random_list(100) #generate the list of 100 random integers
|
||||
total += search.linear_search_count(arr, 42) #search for the value 42 using linear search and add the number of checks it took to find 42 to the total
|
||||
print("Average number of checks for 100 tests:", total / tests) #print the average number of checks it took to find 42 for 100 tests
|
||||
|
||||
"""
|
||||
Part 3: Uses the recursive search
|
||||
"""
|
||||
arr = sorted(search.generate_random_list(100)) #generate the list of 100 random integers and sort it for binary search
|
||||
index = search.binary_search_recursive(arr, 42) #search for the value 42 using binary search and get the index of 42
|
||||
print("Index of 42 in sorted array:", index) #print the index of 42 in the sorted array
|
||||
|
||||
"""
|
||||
Part 4: Tests
|
||||
|
||||
"""
|
||||
arr = search.generate_random_list(n) #generate the list of n random integers
|
||||
|
||||
for multiplier in [0.5, 1, 2, 4]: #I used a "multiplier" to avoid rewriting the code for each query
|
||||
query_count = int(n * multiplier)
|
||||
queries = []
|
||||
|
||||
for _ in range(query_count):
|
||||
queries.append(random.choice(arr))
|
||||
|
||||
# Linear timing
|
||||
start = time.perf_counter() #start time is recorded before any of the queries are searched for using linear search
|
||||
for q in queries:
|
||||
search.linear_search_count(arr, q) #search for each query using linear search on the unsorted array
|
||||
end = time.perf_counter() #end time is recorded after all queries have been searched for using linear search
|
||||
linear_time = end - start
|
||||
|
||||
# Sorting + Binary timing
|
||||
start = time.perf_counter() #start time is recorded before the array is sorted and any of the queries are searched for using binary search
|
||||
sorted_arr = sorted(arr) #the array is sorted before the queries are searched for using binary search, but the time it takes to sort the array is included in the total time for this part
|
||||
for q in queries:
|
||||
search.binary_search_recursive(sorted_arr, q) #search for each query using binary search on the sorted array
|
||||
end = time.perf_counter()
|
||||
binary_time = end - start
|
||||
|
||||
print("\nQueries:", query_count)
|
||||
print("Linear time:", linear_time)
|
||||
print("Sorting + Binary time:", binary_time)
|
||||
|
||||
# only execute when you run this file directily
|
||||
if __name__ == "__main__":
|
||||
|
||||
166
Project 4/LinkedList Template.py
Normal file
166
Project 4/LinkedList Template.py
Normal file
@@ -0,0 +1,166 @@
|
||||
######################################
|
||||
# DCS 229 -- Linked List
|
||||
# Linked List implementation
|
||||
# Date:
|
||||
# Name:
|
||||
# Resources Used:
|
||||
##########################################
|
||||
from __future__ import annotations
|
||||
|
||||
# https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions
|
||||
# Want to define our own custom Exception class...
|
||||
|
||||
class EmptyError(Exception):
|
||||
def __init__(self, message: str) -> None:
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
|
||||
class Node[T]:
|
||||
''' class to implement a single node object in a singly-linked
|
||||
linked list '''
|
||||
def __init__(self, data: T):
|
||||
self.data = data
|
||||
self.next = None # points to another Node object
|
||||
|
||||
def get_data(self) -> T:
|
||||
return self.data
|
||||
|
||||
def set_data(self, value: T) -> None:
|
||||
self.data = value
|
||||
|
||||
def get_next(self) -> Node[T]:
|
||||
return self.next
|
||||
|
||||
def set_next(self, next_node: Node[T]) -> None:
|
||||
self.next = next_node
|
||||
|
||||
class LinkedList[T]:
|
||||
''' class to implement a singly-linked linked list '''
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.head = None # the head pointer in the linked list
|
||||
self.size = 0
|
||||
|
||||
def __len__(self) -> int:
|
||||
''' returns the number of nodes in the linked list
|
||||
|
||||
Returns:
|
||||
int - representing the number of nodes in the list
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
def insert_head(self, value: T) -> None:
|
||||
''' adds the given T-type data value to the front of the linked list
|
||||
Parameters:
|
||||
value: a type T data item to be included as the data in the inserted Node
|
||||
Returns:
|
||||
nothing
|
||||
'''
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
pass
|
||||
|
||||
def insert_tail(self, value: T) -> None:
|
||||
''' adds the given T-type data value to the end of the linked list
|
||||
Parameters:
|
||||
value: a type T data item to be included as the data in the inserted Node
|
||||
Returns:
|
||||
nothing
|
||||
'''
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
pass
|
||||
|
||||
def remove_head(self) -> T:
|
||||
''' removes the first Node in the linked list, returning the data item
|
||||
inside that Node... Remember to handle the special case of an
|
||||
empty list (what should the head pointers be in that case?)
|
||||
and remember to update the head pointer when appropriate.
|
||||
Returns:
|
||||
a T type data item extracted from the removed Node
|
||||
Raises:
|
||||
EmptyError exception if list is empty
|
||||
'''
|
||||
|
||||
# raise an error if list is empty
|
||||
# https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions
|
||||
if self.size == 0:
|
||||
raise EmptyError("Cannot remove from an empy list")
|
||||
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
pass
|
||||
|
||||
def remove_tail(self) -> T:
|
||||
''' removes the last Node in the linked list, returning the data item
|
||||
inside that Node... Remember to handle the special case of an
|
||||
empty list
|
||||
|
||||
Returns:
|
||||
a T type data item extracted from the removed Node
|
||||
Raises:
|
||||
EmptyError exception if list is empty
|
||||
'''
|
||||
# raise an error if list is empty
|
||||
if self.size == 0:
|
||||
raise EmptyError("Cannot remove from an empy list")
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
''' returns a str representation of the linked list data
|
||||
Returns:
|
||||
an str representation of the linked list, showing head pointer
|
||||
and data tiems
|
||||
'''
|
||||
str_ = "head->"
|
||||
|
||||
# start out at the head Node, and walk through Node by Node until we
|
||||
# reach the end of the linked list (i.e., the ._next entry is None)
|
||||
ptr_ = self.head
|
||||
while ptr_ is not None:
|
||||
str_ += "[" + str(ptr_.get_data()) + "]->"
|
||||
ptr_ = ptr_.get_next() # move ptr_ to the next Node in the linked list
|
||||
|
||||
if self.head != None:
|
||||
str_ = str_[:-2] # remove trailing "->"
|
||||
str_ += "<-tail"
|
||||
return str_
|
||||
|
||||
def main():
|
||||
# create a LinkedList and try out some various adds and removes
|
||||
ll = LinkedList()
|
||||
try:
|
||||
ll.remove_head()
|
||||
except EmptyError as err:
|
||||
print(err)
|
||||
|
||||
ll.insert_head(8)
|
||||
assert len(ll) == 1
|
||||
removed = ll.remove_head()
|
||||
assert removed == 8
|
||||
assert len(ll) == 0
|
||||
|
||||
ll.insert_tail(6)
|
||||
ll.insert_tail(7)
|
||||
ll.insert_tail(5)
|
||||
print(ll)
|
||||
assert len(ll) == 3
|
||||
|
||||
removed = ll.remove_tail()
|
||||
assert removed == 5
|
||||
assert len(ll) == 2
|
||||
|
||||
# ADD MORE TESTS
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
225
Project 4/LinkedList.py
Normal file
225
Project 4/LinkedList.py
Normal file
@@ -0,0 +1,225 @@
|
||||
######################################
|
||||
# DCS 229 -- Linked List
|
||||
# Linked List implementation
|
||||
# Date: 03/13/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: https://docs.python.org/3/tutorial/datastructures.html
|
||||
##########################################
|
||||
from __future__ import annotations
|
||||
|
||||
# https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions
|
||||
# Want to define our own custom Exception class...
|
||||
|
||||
class EmptyError(Exception):
|
||||
def __init__(self, message: str) -> None:
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
|
||||
class Node[T]:
|
||||
''' class to implement a single node object in a singly-linked
|
||||
linked list '''
|
||||
def __init__(self, data: T):
|
||||
self.data = data
|
||||
self.next = None # points to another Node object
|
||||
|
||||
def get_data(self) -> T:
|
||||
return self.data
|
||||
|
||||
def set_data(self, value: T) -> None:
|
||||
self.data = value
|
||||
|
||||
def get_next(self) -> Node[T]:
|
||||
return self.next
|
||||
|
||||
def set_next(self, next_node: Node[T]) -> None:
|
||||
self.next = next_node
|
||||
|
||||
class LinkedList[T]:
|
||||
''' class to implement a singly-linked linked list '''
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.head = None # the head pointer in the linked list
|
||||
self.size = 0
|
||||
|
||||
def __len__(self) -> int:
|
||||
''' returns the number of nodes in the linked list
|
||||
|
||||
Returns:
|
||||
int - representing the number of nodes in the list
|
||||
'''
|
||||
return self.size
|
||||
|
||||
|
||||
def insert_head(self, value: T) -> None:
|
||||
''' adds the given T-type data value to the front of the linked list
|
||||
Parameters:
|
||||
value: a type T data item to be included as the data in the inserted Node
|
||||
Returns:
|
||||
nothing
|
||||
'''
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
new_node = Node(value) # create a new node new_node
|
||||
new_node.set_next(self.head) # set the new node's next pointer to the current head of the list
|
||||
self.head = new_node # update the head pointer to point to the new node
|
||||
self.size += 1 # increase the size of the list by 1
|
||||
|
||||
def insert_tail(self, value: T) -> None:
|
||||
''' adds the given T-type data value to the end of the linked list
|
||||
Parameters:
|
||||
value: a type T data item to be included as the data in the inserted Node
|
||||
Returns:
|
||||
nothing
|
||||
'''
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
"""
|
||||
The insert_tail method creates a new node with the given value and adds it to the end of the linked list. If the list is empty, it sets the head pointer to the new node. Otherwise, it traverses the list until it reaches the last node and updates its next pointer to point to the new node. Finally, it increments the size of the list by 1.
|
||||
"""
|
||||
new_node = Node(value) # create a new node new_node
|
||||
|
||||
if self.head is None:
|
||||
self.head = new_node #if the list is empty, set the head pointer to the new node
|
||||
else: #else go through list until we reach end and set the last node's next pointer to the new node
|
||||
current = self.head
|
||||
while current.get_next() is not None:
|
||||
current = current.get_next()
|
||||
current.set_next(new_node)
|
||||
|
||||
self.size += 1 #increase the size of the list by 1
|
||||
|
||||
def remove_head(self) -> T:
|
||||
''' removes the first Node in the linked list, returning the data item
|
||||
inside that Node... Remember to handle the special case of an
|
||||
empty list (what should the head pointers be in that case?)
|
||||
and remember to update the head pointer when appropriate.
|
||||
Returns:
|
||||
a T type data item extracted from the removed Node
|
||||
Raises:
|
||||
EmptyError exception if list is empty
|
||||
'''
|
||||
|
||||
# raise an error if list is empty
|
||||
# https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions
|
||||
if self.size == 0:
|
||||
raise EmptyError("Cannot remove from an empy list")
|
||||
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
removed_node = self.head # store the current head node in a variable removed_node
|
||||
self.head = self.head.get_next() # update the head pointer to point to the next node in the list (which could be None if there was only one node)
|
||||
|
||||
self.size -= 1 #decrease the size of the list by 1
|
||||
return removed_node.get_data() # return the data item from the removed node
|
||||
|
||||
def remove_tail(self) -> T:
|
||||
''' removes the last Node in the linked list, returning the data item
|
||||
inside that Node... Remember to handle the special case of an
|
||||
empty list
|
||||
|
||||
Returns:
|
||||
a T type data item extracted from the removed Node
|
||||
Raises:
|
||||
EmptyError exception if list is empty
|
||||
'''
|
||||
# raise an error if list is empty
|
||||
if self.size == 0:
|
||||
raise EmptyError("Cannot remove from an empy list")
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
"""
|
||||
If the list has only one node, we can simply remove the head and return its data. Otherwise, we need to traverse the list to find the second-to-last node, update its next pointer to None, and return the data from the last node.
|
||||
"""
|
||||
if self.head.get_next() is None: #continue if the list has only one node
|
||||
removed_data = self.head.get_data() #return the data
|
||||
self.head = None #set the head to none
|
||||
self.size -= 1 #decrease the size of the list by 1
|
||||
return removed_data #return the data from the removed node
|
||||
|
||||
current = self.head #start from the head node
|
||||
|
||||
while current.get_next().get_next() is not None: #while the next node's next pointer is not None, keep going
|
||||
current = current.get_next() #move current to the next node
|
||||
|
||||
removed_data = current.get_next().get_data()
|
||||
current.set_next(None)
|
||||
|
||||
self.size -= 1 #decrease the size of the list by 1
|
||||
return removed_data #return the data from the removed node
|
||||
|
||||
|
||||
def __str__(self):
|
||||
''' returns a str representation of the linked list data
|
||||
Returns:
|
||||
an str representation of the linked list, showing head pointer
|
||||
and data tiems
|
||||
'''
|
||||
str_ = "head->"
|
||||
|
||||
# start out at the head Node, and walk through Node by Node until we
|
||||
# reach the end of the linked list (i.e., the ._next entry is None)
|
||||
ptr_ = self.head
|
||||
while ptr_ is not None:
|
||||
str_ += "[" + str(ptr_.get_data()) + "]->"
|
||||
ptr_ = ptr_.get_next() # move ptr_ to the next Node in the linked list
|
||||
|
||||
if self.head != None:
|
||||
str_ = str_[:-2] # remove trailing "->"
|
||||
str_ += "<-tail"
|
||||
return str_
|
||||
|
||||
def main():
|
||||
# create a LinkedList and try out some various adds and removes
|
||||
ll = LinkedList()
|
||||
try:
|
||||
ll.remove_head()
|
||||
except EmptyError as err:
|
||||
print(err)
|
||||
|
||||
ll.insert_head(8)
|
||||
assert len(ll) == 1
|
||||
removed = ll.remove_head()
|
||||
assert removed == 8
|
||||
assert len(ll) == 0
|
||||
|
||||
ll.insert_tail(6)
|
||||
ll.insert_tail(7)
|
||||
ll.insert_tail(5)
|
||||
print(ll)
|
||||
assert len(ll) == 3
|
||||
|
||||
removed = ll.remove_tail()
|
||||
assert removed == 5
|
||||
assert len(ll) == 2
|
||||
|
||||
# ADD MORE TESTS
|
||||
|
||||
# test inserting multiple at head
|
||||
ll.insert_head(1)
|
||||
ll.insert_head(2)
|
||||
ll.insert_head(3)
|
||||
assert len(ll) == 5
|
||||
print(ll)
|
||||
|
||||
# test removing head repeatedly
|
||||
assert ll.remove_head() == 3
|
||||
assert ll.remove_head() == 2
|
||||
assert len(ll) == 3
|
||||
|
||||
# test removing tail until empty
|
||||
assert ll.remove_tail() == 7
|
||||
assert ll.remove_tail() == 6
|
||||
assert ll.remove_tail() == 1
|
||||
assert len(ll) == 0
|
||||
|
||||
# test removing from empty again
|
||||
try:
|
||||
ll.remove_tail()
|
||||
except EmptyError:
|
||||
print("Correctly caught empty list error")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
Project 4/Project 4.pdf
Normal file
BIN
Project 4/Project 4.pdf
Normal file
Binary file not shown.
229
Project 5/LinkedList.py
Normal file
229
Project 5/LinkedList.py
Normal file
@@ -0,0 +1,229 @@
|
||||
"""
|
||||
This file is duplicated from my Project 4 repo and is identical
|
||||
"""
|
||||
|
||||
######################################
|
||||
# DCS 229 -- Linked List
|
||||
# Linked List implementation
|
||||
# Date: 03/13/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: https://docs.python.org/3/tutorial/datastructures.html
|
||||
##########################################
|
||||
from __future__ import annotations
|
||||
|
||||
# https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions
|
||||
# Want to define our own custom Exception class...
|
||||
|
||||
class EmptyError(Exception):
|
||||
def __init__(self, message: str) -> None:
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
|
||||
class Node[T]:
|
||||
''' class to implement a single node object in a singly-linked
|
||||
linked list '''
|
||||
def __init__(self, data: T):
|
||||
self.data = data
|
||||
self.next = None # points to another Node object
|
||||
|
||||
def get_data(self) -> T:
|
||||
return self.data
|
||||
|
||||
def set_data(self, value: T) -> None:
|
||||
self.data = value
|
||||
|
||||
def get_next(self) -> Node[T]:
|
||||
return self.next
|
||||
|
||||
def set_next(self, next_node: Node[T]) -> None:
|
||||
self.next = next_node
|
||||
|
||||
class LinkedList[T]:
|
||||
''' class to implement a singly-linked linked list '''
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.head = None # the head pointer in the linked list
|
||||
self.size = 0
|
||||
|
||||
def __len__(self) -> int:
|
||||
''' returns the number of nodes in the linked list
|
||||
|
||||
Returns:
|
||||
int - representing the number of nodes in the list
|
||||
'''
|
||||
return self.size
|
||||
|
||||
|
||||
def insert_head(self, value: T) -> None:
|
||||
''' adds the given T-type data value to the front of the linked list
|
||||
Parameters:
|
||||
value: a type T data item to be included as the data in the inserted Node
|
||||
Returns:
|
||||
nothing
|
||||
'''
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
new_node = Node(value) # create a new node new_node
|
||||
new_node.set_next(self.head) # set the new node's next pointer to the current head of the list
|
||||
self.head = new_node # update the head pointer to point to the new node
|
||||
self.size += 1 # increase the size of the list by 1
|
||||
|
||||
def insert_tail(self, value: T) -> None:
|
||||
''' adds the given T-type data value to the end of the linked list
|
||||
Parameters:
|
||||
value: a type T data item to be included as the data in the inserted Node
|
||||
Returns:
|
||||
nothing
|
||||
'''
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
"""
|
||||
The insert_tail method creates a new node with the given value and adds it to the end of the linked list. If the list is empty, it sets the head pointer to the new node. Otherwise, it traverses the list until it reaches the last node and updates its next pointer to point to the new node. Finally, it increments the size of the list by 1.
|
||||
"""
|
||||
new_node = Node(value) # create a new node new_node
|
||||
|
||||
if self.head is None:
|
||||
self.head = new_node #if the list is empty, set the head pointer to the new node
|
||||
else: #else go through list until we reach end and set the last node's next pointer to the new node
|
||||
current = self.head
|
||||
while current.get_next() is not None:
|
||||
current = current.get_next()
|
||||
current.set_next(new_node)
|
||||
|
||||
self.size += 1 #increase the size of the list by 1
|
||||
|
||||
def remove_head(self) -> T:
|
||||
''' removes the first Node in the linked list, returning the data item
|
||||
inside that Node... Remember to handle the special case of an
|
||||
empty list (what should the head pointers be in that case?)
|
||||
and remember to update the head pointer when appropriate.
|
||||
Returns:
|
||||
a T type data item extracted from the removed Node
|
||||
Raises:
|
||||
EmptyError exception if list is empty
|
||||
'''
|
||||
|
||||
# raise an error if list is empty
|
||||
# https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions
|
||||
if self.size == 0:
|
||||
raise EmptyError("Cannot remove from an empy list")
|
||||
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
removed_node = self.head # store the current head node in a variable removed_node
|
||||
self.head = self.head.get_next() # update the head pointer to point to the next node in the list (which could be None if there was only one node)
|
||||
|
||||
self.size -= 1 #decrease the size of the list by 1
|
||||
return removed_node.get_data() # return the data item from the removed node
|
||||
|
||||
def remove_tail(self) -> T:
|
||||
''' removes the last Node in the linked list, returning the data item
|
||||
inside that Node... Remember to handle the special case of an
|
||||
empty list
|
||||
|
||||
Returns:
|
||||
a T type data item extracted from the removed Node
|
||||
Raises:
|
||||
EmptyError exception if list is empty
|
||||
'''
|
||||
# raise an error if list is empty
|
||||
if self.size == 0:
|
||||
raise EmptyError("Cannot remove from an empy list")
|
||||
|
||||
## YOUR CODE HERE ##
|
||||
|
||||
"""
|
||||
If the list has only one node, we can simply remove the head and return its data. Otherwise, we need to traverse the list to find the second-to-last node, update its next pointer to None, and return the data from the last node.
|
||||
"""
|
||||
if self.head.get_next() is None: #continue if the list has only one node
|
||||
removed_data = self.head.get_data() #return the data
|
||||
self.head = None #set the head to none
|
||||
self.size -= 1 #decrease the size of the list by 1
|
||||
return removed_data #return the data from the removed node
|
||||
|
||||
current = self.head #start from the head node
|
||||
|
||||
while current.get_next().get_next() is not None: #while the next node's next pointer is not None, keep going
|
||||
current = current.get_next() #move current to the next node
|
||||
|
||||
removed_data = current.get_next().get_data()
|
||||
current.set_next(None)
|
||||
|
||||
self.size -= 1 #decrease the size of the list by 1
|
||||
return removed_data #return the data from the removed node
|
||||
|
||||
|
||||
def __str__(self):
|
||||
''' returns a str representation of the linked list data
|
||||
Returns:
|
||||
an str representation of the linked list, showing head pointer
|
||||
and data tiems
|
||||
'''
|
||||
str_ = "head->"
|
||||
|
||||
# start out at the head Node, and walk through Node by Node until we
|
||||
# reach the end of the linked list (i.e., the ._next entry is None)
|
||||
ptr_ = self.head
|
||||
while ptr_ is not None:
|
||||
str_ += "[" + str(ptr_.get_data()) + "]->"
|
||||
ptr_ = ptr_.get_next() # move ptr_ to the next Node in the linked list
|
||||
|
||||
if self.head != None:
|
||||
str_ = str_[:-2] # remove trailing "->"
|
||||
str_ += "<-tail"
|
||||
return str_
|
||||
|
||||
def main():
|
||||
# create a LinkedList and try out some various adds and removes
|
||||
ll = LinkedList()
|
||||
try:
|
||||
ll.remove_head()
|
||||
except EmptyError as err:
|
||||
print(err)
|
||||
|
||||
ll.insert_head(8)
|
||||
assert len(ll) == 1
|
||||
removed = ll.remove_head()
|
||||
assert removed == 8
|
||||
assert len(ll) == 0
|
||||
|
||||
ll.insert_tail(6)
|
||||
ll.insert_tail(7)
|
||||
ll.insert_tail(5)
|
||||
print(ll)
|
||||
assert len(ll) == 3
|
||||
|
||||
removed = ll.remove_tail()
|
||||
assert removed == 5
|
||||
assert len(ll) == 2
|
||||
|
||||
# ADD MORE TESTS
|
||||
|
||||
# test inserting multiple at head
|
||||
ll.insert_head(1)
|
||||
ll.insert_head(2)
|
||||
ll.insert_head(3)
|
||||
assert len(ll) == 5
|
||||
print(ll)
|
||||
|
||||
# test removing head repeatedly
|
||||
assert ll.remove_head() == 3
|
||||
assert ll.remove_head() == 2
|
||||
assert len(ll) == 3
|
||||
|
||||
# test removing tail until empty
|
||||
assert ll.remove_tail() == 7
|
||||
assert ll.remove_tail() == 6
|
||||
assert ll.remove_tail() == 1
|
||||
assert len(ll) == 0
|
||||
|
||||
# test removing from empty again
|
||||
try:
|
||||
ll.remove_tail()
|
||||
except EmptyError:
|
||||
print("Correctly caught empty list error")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
Project 5/Project 5_ Turtle Maze.pdf
Normal file
BIN
Project 5/Project 5_ Turtle Maze.pdf
Normal file
Binary file not shown.
116
Project 5/Stack.py
Normal file
116
Project 5/Stack.py
Normal file
@@ -0,0 +1,116 @@
|
||||
######################################
|
||||
# DCS 229 -- Stack
|
||||
# Stack data structure implemented with Linked List
|
||||
# Date: 03/29/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: LinkedList.py from Project 4. I also worked with Pat Cohen on the project.
|
||||
##########################################
|
||||
|
||||
|
||||
from LinkedList import *
|
||||
|
||||
|
||||
class EmptyError(Exception):
|
||||
''' class extending Exception to better document stack errors '''
|
||||
def __init__(self, message: str):
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
|
||||
|
||||
class Stack[T]:
|
||||
''' class to implement a stack ADT using our LinkedList class'''
|
||||
|
||||
|
||||
def __init__(self):
|
||||
''' initializes an empty stack '''
|
||||
self._data = LinkedList()
|
||||
|
||||
def __len__(self) -> int:
|
||||
''' allows the len function to be called using a Stack object, e.g.,
|
||||
stack = Stack()
|
||||
print(len(stack))
|
||||
Returns:
|
||||
number of elements in the stack, as an integer
|
||||
'''
|
||||
return len(self._data)
|
||||
|
||||
def push(self, item: T) -> None:
|
||||
''' pushes a given item of arbitrary type onto the stack
|
||||
Parameters:
|
||||
item: an item of arbitrary type
|
||||
Returns:
|
||||
None
|
||||
'''
|
||||
self._data.insert_head(item)
|
||||
|
||||
def pop(self) -> T:
|
||||
''' removes the topmost element from the stack and returns that element
|
||||
Returns:
|
||||
the topmost item, of arbitrary type
|
||||
Raises:
|
||||
EmptyError exception if the stack is empty
|
||||
'''
|
||||
if self.is_empty():
|
||||
raise EmptyError("Stack is empty")
|
||||
return self._data.remove_head()
|
||||
|
||||
def peek(self) -> T:
|
||||
''' returns the topmost element from the stack without modifying the stack
|
||||
Returns:
|
||||
the topmost item, of arbitrary type
|
||||
Raises:
|
||||
EmptyError exception if the stack is empty
|
||||
'''
|
||||
if self.is_empty():
|
||||
raise EmptyError("Stack is empty")
|
||||
return self._data.head.get_data()
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
''' indicates whether the stack is empty
|
||||
Returns:
|
||||
True if the stack is empty, False otherwise
|
||||
'''
|
||||
return len(self._data) == 0
|
||||
|
||||
def __str__(self) -> str:
|
||||
''' returns an str implementation of the Stack '''
|
||||
|
||||
return str(self._data)
|
||||
|
||||
def main():
|
||||
'''test functions'''
|
||||
s = Stack() #I added this so stack doesnt need to be reinitalized for each test
|
||||
|
||||
assert len(s) == 0
|
||||
assert s.is_empty() is True
|
||||
|
||||
s.push(10)
|
||||
assert len(s) == 1
|
||||
assert s.peek() == 10
|
||||
|
||||
s.push(20)
|
||||
s.push(30)
|
||||
assert len(s) == 3
|
||||
assert s.peek() == 30
|
||||
|
||||
assert s.pop() == 30
|
||||
assert s.pop() == 20
|
||||
assert s.pop() == 10
|
||||
assert s.is_empty() is True
|
||||
|
||||
try:
|
||||
s.pop()
|
||||
assert False
|
||||
except EmptyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
s.peek()
|
||||
assert False
|
||||
except EmptyError:
|
||||
pass
|
||||
|
||||
print("All Stack tests passed.") #Only runs if all previous tests pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
107
Project 5/TurtleDFS.py
Normal file
107
Project 5/TurtleDFS.py
Normal file
@@ -0,0 +1,107 @@
|
||||
######################################
|
||||
# DCS 229 -- TurtleDFS
|
||||
# Depth-First Search implementation for Turtle Maze
|
||||
# Date: 03/29/2026
|
||||
# Name: Benjamin Adovasio
|
||||
# Resources Used: I worked with Pat Cohen on the project.
|
||||
##########################################
|
||||
|
||||
import os
|
||||
from Stack import Stack
|
||||
from TurtleMaze import TurtleMaze, Cell, Contents
|
||||
|
||||
def get_neighbors(maze: TurtleMaze, cell: Cell) -> list[Cell]:
|
||||
'''returns a list of non-obstacle neighbors in north, south, east, west order'''
|
||||
|
||||
neighbors = []
|
||||
row = cell.y
|
||||
col = cell.x
|
||||
|
||||
# north
|
||||
if row - 1 >= 0:
|
||||
north = maze[row - 1][col]
|
||||
if not north.isBlocked():
|
||||
neighbors.append(north)
|
||||
|
||||
# south
|
||||
if row + 1 < len(maze.maze_grid):
|
||||
south = maze[row + 1][col]
|
||||
if not south.isBlocked():
|
||||
neighbors.append(south)
|
||||
|
||||
# east
|
||||
if col + 1 < len(maze[0]):
|
||||
east = maze[row][col + 1]
|
||||
if not east.isBlocked():
|
||||
neighbors.append(east)
|
||||
|
||||
# west
|
||||
if col - 1 >= 0:
|
||||
west = maze[row][col - 1]
|
||||
if not west.isBlocked():
|
||||
neighbors.append(west)
|
||||
|
||||
return neighbors
|
||||
|
||||
def dfs(maze: TurtleMaze) -> bool:
|
||||
'''runs a non-recursive depth first search on the maze with backtracking
|
||||
Returns:
|
||||
True if the goal is found, False otherwise
|
||||
'''
|
||||
|
||||
stack = Stack()
|
||||
start = maze.getStart()
|
||||
visited = set()
|
||||
|
||||
stack.push(start)
|
||||
visited.add((start.y, start.x))
|
||||
maze.updatePosition(start)
|
||||
|
||||
while not stack.is_empty():
|
||||
current = stack.peek()
|
||||
|
||||
# if we reached the goal, we are done
|
||||
if current.isGoal():
|
||||
return True
|
||||
|
||||
found_next = False
|
||||
|
||||
# look for an unvisited neighbor
|
||||
for neighbor in get_neighbors(maze, current):
|
||||
if (neighbor.y, neighbor.x) not in visited:
|
||||
neighbor.setParent(current)
|
||||
visited.add((neighbor.y, neighbor.x))
|
||||
stack.push(neighbor)
|
||||
|
||||
if not neighbor.isGoal():
|
||||
maze.updatePosition(neighbor, Contents.TRIED)
|
||||
else:
|
||||
maze.updatePosition(neighbor)
|
||||
|
||||
found_next = True
|
||||
break
|
||||
|
||||
# if no unvisited neighbor exists, backtrack
|
||||
if not found_next:
|
||||
dead_end = stack.pop()
|
||||
|
||||
if dead_end != start and not dead_end.isGoal():
|
||||
maze.updatePosition(dead_end, Contents.DEAD_END)
|
||||
|
||||
if not stack.is_empty():
|
||||
maze.updatePosition(stack.peek())
|
||||
|
||||
return False
|
||||
|
||||
def main():
|
||||
maze = TurtleMaze('maze_1.txt')
|
||||
maze.drawMaze()
|
||||
|
||||
solved = dfs(maze)
|
||||
print("Maze solved:", solved)
|
||||
|
||||
maze.t.screen.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
261
Project 5/TurtleMaze.py
Normal file
261
Project 5/TurtleMaze.py
Normal file
@@ -0,0 +1,261 @@
|
||||
######################################
|
||||
# DCS 229 -- TurtleMaze
|
||||
# Utility file with the TurtleMaze class.
|
||||
# Date: March 13, 2026
|
||||
# Name: Prof. Andy Ricci
|
||||
# Resources Used: https://runestone.academy/ns/books/published/pythonds/Recursion/ExploringaMaze.html. I also worked with Pat Cohen on the project.
|
||||
##########################################
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
import turtle
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
class Contents(str, Enum):
|
||||
''' create an enumeration to define what the visual contents of a Cell are;
|
||||
using str as a "mixin" (multiple inheritance) forces all the entries to
|
||||
be strings; using an Enum means no cell entry can be anything other
|
||||
than the options here
|
||||
'''
|
||||
|
||||
EMPTY = ' '
|
||||
START = 'S'
|
||||
GOAL = 'G'
|
||||
PART_OF_PATH = 'O'
|
||||
TRIED = '.'
|
||||
OBSTACLE = '+'
|
||||
DEAD_END = '-'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
|
||||
|
||||
################################################################################
|
||||
class Cell:
|
||||
''' class that allows us to use Cell as a data type -- an ordered triple
|
||||
of row, column, & cell contents
|
||||
(see Contents class enumeration above)
|
||||
'''
|
||||
|
||||
def __init__(self, row: int, col: int, contents: Contents):
|
||||
self._contents: Contents = contents
|
||||
self._parent: Cell = None # parent of this Cell during exploration
|
||||
self.x = col
|
||||
self.y = row
|
||||
|
||||
def getParent(self) -> Cell:
|
||||
''' method to return the parent of this Cell object as determined during
|
||||
maze exploration
|
||||
Returns:
|
||||
a Cell object corresponding to the cell that considered this cell
|
||||
during the exploration process
|
||||
'''
|
||||
return self._parent
|
||||
|
||||
def setParent(self, parent: Cell) -> None:
|
||||
''' setter method to update this Cell's parent
|
||||
Parameters:
|
||||
parent: a different Cell object
|
||||
Raises:
|
||||
ValueError if self == parent
|
||||
'''
|
||||
if self == parent: raise ValueError(f"a Cell cannot be its own parent")
|
||||
self._parent = parent
|
||||
|
||||
def markOnPath(self) -> None:
|
||||
''' method to identify this cell as being on the path from source
|
||||
to goal
|
||||
'''
|
||||
self._contents = Contents.PART_OF_PATH
|
||||
|
||||
def isBlocked(self) -> bool:
|
||||
''' Boolean method to indicate whether this cell contains a block
|
||||
Returns:
|
||||
True if the cell is blocked (cannot be explored), False o/w
|
||||
'''
|
||||
return self._contents == Contents.OBSTACLE
|
||||
|
||||
def isGoal(self) -> bool:
|
||||
''' Boolean method to indicate whether this cell is the goal
|
||||
Returns:
|
||||
True if the cell is the maze goal, False o/w
|
||||
'''
|
||||
return self._contents == Contents.GOAL
|
||||
|
||||
def __str__(self) -> str:
|
||||
''' creates and returns a string representation of this cell
|
||||
Returns:
|
||||
a string identifying the cell's row, col, and cell contents
|
||||
'''
|
||||
string = f"({self.x}, {self.y}, {self._contents}) "
|
||||
if self._parent is not None:
|
||||
string += f"Parent is: ({self._parent.x}, {self._parent.y})"
|
||||
return string
|
||||
|
||||
def __repr__(self) -> str:
|
||||
''' overriding __repr__ so that printing, e.g., a list of Cell objects
|
||||
(which will call __repr__ for each) will call __str__ for each, printing
|
||||
nicely
|
||||
Returns:
|
||||
a string identifying the cell's row, col, and cell contents
|
||||
'''
|
||||
return self.__str__()
|
||||
|
||||
def __eq__(self, other: Cell) -> bool:
|
||||
''' indicates whether a given other Cell is equal to this Cell
|
||||
Returns:
|
||||
True if this Cell and the other Cell are the same, False o/w
|
||||
'''
|
||||
return self.x == other.x and \
|
||||
self.y == other.y
|
||||
|
||||
|
||||
################################################################################
|
||||
class TurtleMaze:
|
||||
def __init__(self, mazeFileName):
|
||||
rowsInMaze = 0
|
||||
columnsInMaze = 0
|
||||
self.maze_grid = []
|
||||
mazeFile = open(mazeFileName, 'r')
|
||||
rowsInMaze = 0
|
||||
for line in mazeFile:
|
||||
rowList = []
|
||||
col = 0
|
||||
for ch in line[:-1]: #
|
||||
# make new cell: (row, col, contents)
|
||||
cell = Cell(rowsInMaze, col, ch)
|
||||
rowList.append(cell)
|
||||
if ch == Contents.START:
|
||||
self.startRow = rowsInMaze
|
||||
self.startCol = col
|
||||
self._start = cell
|
||||
if ch == Contents.GOAL:
|
||||
self._goal = cell
|
||||
|
||||
col = col + 1
|
||||
rowsInMaze = rowsInMaze + 1
|
||||
self.maze_grid.append(rowList)
|
||||
columnsInMaze = len(rowList)
|
||||
|
||||
self._num_rows = rowsInMaze
|
||||
self._num_cols = columnsInMaze
|
||||
self.xTranslate = -columnsInMaze / 2
|
||||
self.yTranslate = rowsInMaze / 2
|
||||
self.t = turtle.Turtle()
|
||||
self.t.shape('turtle')
|
||||
self.wn = turtle.Screen()
|
||||
self.wn.setworldcoordinates(-(columnsInMaze - 1) / 2 - .5, -(rowsInMaze - 1) / 2 - .5,
|
||||
(columnsInMaze - 1) / 2 + .5, (rowsInMaze - 1) / 2 + .5)
|
||||
|
||||
|
||||
def drawMaze(self):
|
||||
self.t.speed(10)
|
||||
self.wn.tracer(0)
|
||||
for y in range(self._num_rows):
|
||||
for x in range(self._num_cols):
|
||||
cell = self.maze_grid[y][x]
|
||||
if cell._contents == Contents.OBSTACLE:
|
||||
self.drawCenteredBox(x + self.xTranslate, -y + self.yTranslate, 'orange')
|
||||
self.updatePosition(self.getStart())
|
||||
self.t.color('black')
|
||||
self.t.fillcolor('blue')
|
||||
self.wn.update()
|
||||
self.wn.tracer(1)
|
||||
|
||||
def drawCenteredBox(self, x, y, color):
|
||||
self.t.up()
|
||||
self.t.goto(x - .5, y - .5)
|
||||
self.t.color(color)
|
||||
self.t.fillcolor(color)
|
||||
self.t.setheading(90)
|
||||
self.t.down()
|
||||
self.t.begin_fill()
|
||||
for i in range(4):
|
||||
self.t.forward(1)
|
||||
self.t.right(90)
|
||||
self.t.end_fill()
|
||||
|
||||
def moveTurtle(self, x, y):
|
||||
self.t.up()
|
||||
self.t.setheading(self.t.towards(x + self.xTranslate, -y + self.yTranslate))
|
||||
self.t.goto(x + self.xTranslate, -y + self.yTranslate)
|
||||
|
||||
def dropBreadcrumb(self, color):
|
||||
self.t.dot(10, color)
|
||||
|
||||
def updatePosition(self, cell, val=None):
|
||||
if val:
|
||||
cell._contents = val
|
||||
self.moveTurtle(cell.x, cell.y)
|
||||
|
||||
if val == Contents.PART_OF_PATH:
|
||||
color = 'green'
|
||||
elif val == Contents.TRIED:
|
||||
color = 'black'
|
||||
elif val == Contents.DEAD_END:
|
||||
color = 'red'
|
||||
else:
|
||||
color = None
|
||||
|
||||
if color:
|
||||
self.dropBreadcrumb(color)
|
||||
|
||||
|
||||
def getStart(self) -> Cell:
|
||||
''' accessor method to return the Cell object corresponding to the Maze start
|
||||
Returns:
|
||||
the Cell object at the Maze start location
|
||||
'''
|
||||
return self._start
|
||||
|
||||
def getGoal(self) -> Cell:
|
||||
''' accessor method to return the Cell object corresponding to the Maze goal
|
||||
Returns:
|
||||
the Cell object at the Maze goal location
|
||||
'''
|
||||
return self._goal
|
||||
|
||||
def __getitem__(self, idx):
|
||||
"""
|
||||
Magic method for accessing element with square brackets []
|
||||
|
||||
Example usage:
|
||||
|
||||
maze = TurtleMaze(file)
|
||||
maze[4] # the row at index 4 (the 5th row in the maze)
|
||||
maze[4][0] # the first cell in the row at index 4
|
||||
"""
|
||||
|
||||
return self.maze_grid[idx]
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
maze = TurtleMaze('maze_2.txt')
|
||||
maze.drawMaze()
|
||||
|
||||
# print the contents of the top-right corner
|
||||
top_right = maze[0][len(maze[0]) - 1]
|
||||
print("Top-right corner:", top_right)
|
||||
|
||||
# print the start and goal cells
|
||||
print("Start cell:", maze.getStart())
|
||||
print("Goal cell:", maze.getGoal())
|
||||
|
||||
# start at S
|
||||
maze.updatePosition(maze.getStart())
|
||||
|
||||
# move only to neighboring cells so the turtle stays on the path
|
||||
maze.updatePosition(maze[9][15], Contents.TRIED) # down 1
|
||||
maze.updatePosition(maze[9][16], Contents.PART_OF_PATH) # right 1
|
||||
maze.updatePosition(maze[9][17], Contents.PART_OF_PATH) # right 1
|
||||
maze.updatePosition(maze[8][17], Contents.DEAD_END) # up 1
|
||||
|
||||
maze.t.screen.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
Project 5/__pycache__/LinkedList.cpython-314.pyc
Normal file
BIN
Project 5/__pycache__/LinkedList.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Project 5/__pycache__/Stack.cpython-314.pyc
Normal file
BIN
Project 5/__pycache__/Stack.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Project 5/__pycache__/TurtleDFS.cpython-314.pyc
Normal file
BIN
Project 5/__pycache__/TurtleDFS.cpython-314.pyc
Normal file
Binary file not shown.
BIN
Project 5/__pycache__/TurtleMaze.cpython-314.pyc
Normal file
BIN
Project 5/__pycache__/TurtleMaze.cpython-314.pyc
Normal file
Binary file not shown.
7
Project 5/maze_1.txt
Normal file
7
Project 5/maze_1.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
+++++++++
|
||||
+ S ++
|
||||
++++++ ++
|
||||
+G+ ++
|
||||
+ + + ++
|
||||
++ + ++
|
||||
+++++++++
|
||||
11
Project 5/maze_2.txt
Normal file
11
Project 5/maze_2.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
++++++++++++++++++++++
|
||||
+ + ++ ++ +
|
||||
G + ++++++++++
|
||||
+ + ++ ++++ +++ ++
|
||||
+ + + + ++ +++ +
|
||||
+ ++ ++ + +
|
||||
+++++ + + ++ + +
|
||||
+++++ +++ + + ++ +
|
||||
+ + + S+ + +
|
||||
+++++ + + + + + +
|
||||
++++++++++++++++++++++
|
||||
3
Project 5/maze_3.txt
Normal file
3
Project 5/maze_3.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
++++++++++++++++++++++
|
||||
+G S++
|
||||
++++++++++++++++++++++
|
||||
Reference in New Issue
Block a user