[+] A1 Starter Files

This commit is contained in:
Hykilpikonna
2022-01-21 15:01:51 -05:00
parent c549bca950
commit 87ac102bfe
5 changed files with 633 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
\documentclass[fontsize=11pt]{article}
\usepackage{amsmath}
\usepackage[utf8]{inputenc}
\usepackage[margin=0.75in]{geometry}
\title{CSC111 Assignment 1: Linked Lists}
\author{TODO: FILL IN THE NAME OF EACH OF YOUR GROUP MEMBERS HERE}
\date{\today}
\begin{document}
\maketitle
\section*{Part 1: Faster Searching in Linked Lists}
\begin{enumerate}
\item[1.]
Complete this part in the provided \texttt{a1\_part1.py} starter file.
Do \textbf{not} include your solution in this file.
\item[2.]
Complete this part in the provided \texttt{a1\_part1\_test.py} starter file.
Do \textbf{not} include your solution in this file.
\item[3.]
\begin{enumerate}
\item[(a)]
TODO: Write your answer here.
\item[(b)]
TODO: Write your answer here.
\end{enumerate}
\item[4.]
TODO: Write your answer here.
\end{enumerate}
\section*{Part 2: Linked List Visualization}
Complete this part in the provided \texttt{a1\_part2.py} starter file.
Do \textbf{not} include your solution in this file.
\end{document}
+169
View File
@@ -0,0 +1,169 @@
"""CSC111 Winter 2021 Assignment 1: Linked Lists
Do not change this file -- it will not be submitted, and we will supply our own copy
for grading purposes.
Copyright and Usage Information
===============================
This file is provided solely for the personal and private use of students
taking CSC111 at the University of Toronto St. George campus. All forms of
distribution of this code, whether as given or with any changes, are
expressly prohibited. For more information on copyright for CSC111 materials,
please consult our Course Syllabus.
This file is Copyright (c) 2021 David Liu and Isaac Waller.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Iterable, Optional
@dataclass
class _Node:
"""A node in a linked list.
Note that this is considered a "private class", one which is only meant
to be used in this module by the LinkedList class, but not by client code.
Instance Attributes:
- item: The data stored in this node.
- next: The next node in the list, if any.
"""
item: Any
next: Optional[_Node] = None # By default, this node does not link to any other node
class LinkedList:
"""A linked list implementation of the List ADT.
"""
# Private Instance Attributes:
# - _first: The first node in the linked list, or None if the list is empty.
_first: Optional[_Node]
def __init__(self, items: Iterable) -> None:
"""Initialize a new linked list containing the given items.
"""
self._first = None
for item in items:
self.append(item)
def to_list(self) -> list:
"""Return a built-in Python list containing the items of this linked list.
The items in this linked list appear in the same order in the returned list.
"""
items_so_far = []
curr = self._first
while curr is not None:
items_so_far.append(curr.item)
curr = curr.next
return items_so_far
def __len__(self) -> int:
"""Return the number of elements in this list.
>>> lst = LinkedList([])
>>> len(lst) # Equivalent to lst.__len__()
0
>>> lst = LinkedList([1, 2, 3])
>>> len(lst)
3
"""
curr = self._first
len_so_far = 0
while curr is not None:
len_so_far += 1
curr = curr.next
return len_so_far
def __contains__(self, item: Any) -> bool:
"""Return whether item is in this linked list.
"""
curr = self._first
while curr is not None:
if curr.item == item:
return True
curr = curr.next
return False
def __getitem__(self, i: int) -> Any:
"""Return the item stored at index i in this linked list.
Raise an IndexError if index i is out of bounds.
Preconditions:
- i >= 0
"""
curr = self._first
curr_index = 0
while curr is not None:
if curr_index == i:
return curr.item
curr = curr.next
curr_index = curr_index + 1
# If we've reached the end of the list and no item has been returned,
# the given index is out of bounds.
raise IndexError
def pop(self, i: int) -> Any:
"""Remove and return the item at index i.
Raise IndexError if i >= len(self).
Preconditions:
- i >= 0
>>> lst = LinkedList([1, 2, 10, 200])
>>> lst.pop(1)
2
>>> lst.to_list()
[1, 10, 200]
"""
if i == 0:
if self._first is None:
raise IndexError
else:
item = self._first.item
self._first = self._first.next
return item
else:
curr = self._first
curr_index = 0
while not (curr is None or curr_index == i - 1):
curr = curr.next
curr_index = curr_index + 1
if curr is None:
raise IndexError
else:
if curr.next is None:
raise IndexError
else:
item = curr.next.item
curr.next = curr.next.next
return item
def append(self, item: Any) -> None:
"""Add the given item to the end of this linked list.
"""
new_node = _Node(item)
if self._first is None:
self._first = new_node
else:
curr = self._first
while curr.next is not None:
curr = curr.next
# After the loop, curr is the last node in the LinkedList.
assert curr is not None and curr.next is None
curr.next = new_node
+132
View File
@@ -0,0 +1,132 @@
"""CSC111 Winter 2021 Assignment 1: Linked Lists, Part 1
Instructions (READ THIS FIRST!)
===============================
This Python module contains three linked list subclasses corresponding to the three
moving heuristics described on the assignment handout.
You need to complete their implementations by overriding the __contains__ method
(and only for CountLinkedList, additional methods as required).
Copyright and Usage Information
===============================
This file is provided solely for the personal and private use of students
taking CSC111 at the University of Toronto St. George campus. All forms of
distribution of this code, whether as given or with any changes, are
expressly prohibited. For more information on copyright for CSC111 materials,
please consult our Course Syllabus.
This file is Copyright (c) 2021 David Liu and Isaac Waller.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Optional
from a1_linked_list import LinkedList, _Node
################################################################################
# Heuristic 1 (move to front)
################################################################################
class MoveToFrontLinkedList(LinkedList):
"""A linked list implementation that uses a "move to front" heuristic for searches.
Representation Invariants:
- all items in this linked list are unique
"""
def __contains__(self, item: Any) -> bool:
"""Return whether item is in this linked list.
If the item is found, move it to the front of this list.
>>> linky = MoveToFrontLinkedList([10, 20, 30, 40, 50, 60])
>>> linky.__contains__(40)
True
>>> linky.to_list()
[40, 10, 20, 30, 50, 60]
"""
################################################################################
# Heuristic 2 (swap)
################################################################################
class SwapLinkedList(LinkedList):
"""A linked list implementation that uses a "swap" heuristic for searches.
Representation Invariants:
- all items in this linked list are unique
"""
def __contains__(self, item: Any) -> bool:
"""Return whether item is in this linked list.
If the item is found, swap it with the item before it, if any.
You may do this by reassigning _Node item or next attributes (or both).
>>> linky = SwapLinkedList([10, 20, 30, 40, 50, 60])
>>> linky.__contains__(40)
True
>>> linky.to_list()
[10, 20, 40, 30, 50, 60]
"""
################################################################################
# Heuristic 3 (count)
################################################################################
# NOTE: this heuristic requires a new kind of _Node that has an additional "count" attribute.
@dataclass
class _CountNode(_Node):
"""A node in a CountLinkedList.
Instance Attributes:
- item: The data stored in this node.
- next: The next node in the list, if any.
- access_count: The number of times this node has been accessed (used by the count heuristic)
"""
next: Optional[_CountNode] = None
access_count: int = 0
class CountLinkedList(LinkedList):
"""A linked list implementation that uses a "swap" heuristic for searches.
Representation Invariants:
- all items in this linked list are unique
- nodes are sorted in non-increasing order by access_count
NOTE: In order to make use of the _CountNode class above, you'll need to override every
LinkedList method in a1_linked_list.py that creates new _Node objects to create _CountNode
objects instead. Your code for the overridden methods should be otherwise identical.
"""
_first: Optional[_CountNode]
def __contains__(self, item: Any) -> bool:
"""Return whether item is in this linked list.
If the item is found, increase its count and reorder the nodes in
non-increasing count order---see assignment handout for details.
>>> linky = CountLinkedList([10, 20, 30, 40, 50, 60])
>>> linky.__contains__(40)
True
>>> linky.to_list()
[40, 10, 20, 30, 50, 60]
"""
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'max-line-length': 100,
'disable': ['E1136'],
'extra-imports': ['a1_linked_list'],
'max-nested-blocks': 4
})
import python_ta.contracts
python_ta.contracts.check_all_contracts()
import doctest
doctest.testmod()
+32
View File
@@ -0,0 +1,32 @@
"""CSC111 Winter 2021 Assignment 1: Linked Lists, Part 1
Instructions (READ THIS FIRST!)
===============================
Please write your tests for Part 1 in this module. Make sure to review the assignment
instructions carefully for this part! You may find it helpful to consult this
section of the Course Notes:
https://www.teach.cs.toronto.edu/~csc110y/fall/notes/B-python-libraries/02-pytest.html
While you must include unit tests, you may also use property-based tests in your test suite.
We will *not* be running PythonTA on this file; however, you should follow good programming
design and style in this file anyway, just like you would for all other work.
Copyright and Usage Information
===============================
This file is provided solely for the personal and private use of students
taking CSC111 at the University of Toronto St. George campus. All forms of
distribution of this code, whether as given or with any changes, are
expressly prohibited. For more information on copyright for CSC111 materials,
please consult our Course Syllabus.
This file is Copyright (c) 2021 David Liu and Isaac Waller.
"""
import pytest
from a1_part1 import MoveToFrontLinkedList, SwapLinkedList, CountLinkedList
if __name__ == '__main__':
pytest.main(['a1_part1_test.py', '-v'])
+256
View File
@@ -0,0 +1,256 @@
"""CSC111 Winter 2021 Assignment 1: Linked Lists, Part 2
Instructions (READ THIS FIRST!)
===============================
This Python module generates a graphical representation of linked lists.
You need to implement a few functions that visualize different components of
the list as described in the handout, and that make your visualization interactive.
Copyright and Usage Information
===============================
This file is provided solely for the personal and private use of students
taking CSC111 at the University of Toronto St. George campus. All forms of
distribution of this code, whether as given or with any changes, are
expressly prohibited. For more information on copyright for CSC111 materials,
please consult our Course Syllabus.
This file is Copyright (c) 2021 David Liu and Isaac Waller.
"""
import random
from typing import Tuple
import pygame
from pygame.colordict import THECOLORS
from a1_linked_list import LinkedList, _Node
################################################################################
# Graphics constants
################################################################################
# You should not change SCREEN_SIZE or GRID_SIZE, but may add your own constants underneath.
SCREEN_SIZE = (800, 800) # (width, height)
GRID_SIZE = 8
################################################################################
# Pygame helper functions (don't change these!)
################################################################################
def initialize_screen(screen_size: tuple[int, int], allowed: list) -> pygame.Surface:
"""Initialize pygame and the display window.
allowed is a list of pygame event types that should be listened for while pygame is running.
"""
pygame.display.init()
pygame.font.init()
screen = pygame.display.set_mode(screen_size)
screen.fill(THECOLORS['white'])
pygame.display.flip()
pygame.event.clear()
pygame.event.set_blocked(None)
pygame.event.set_allowed([pygame.QUIT] + allowed)
return screen
def draw_text(screen: pygame.Surface, text: str, pos: tuple[int, int]) -> None:
"""Draw the given text to the pygame screen at the given position.
pos represents the *upper-left corner* of the text.
"""
font = pygame.font.SysFont('inconsolata', 22)
text_surface = font.render(text, True, THECOLORS['black'])
width, height = text_surface.get_size()
screen.blit(text_surface,
pygame.Rect(pos, (pos[0] + width, pos[1] + height)))
def draw_grid(screen: pygame.Surface) -> None:
"""Draws a square grid on the given surface.
The drawn grid has GRID_SIZE columns and rows.
You can use this to help you check whether you are drawing nodes and edges in the right spots.
"""
color = THECOLORS['grey']
width, height = screen.get_size()
for col in range(1, GRID_SIZE):
x = col * (width // GRID_SIZE)
pygame.draw.line(screen, color, (x, 0), (x, height))
for row in range(1, GRID_SIZE):
y = row * (height // GRID_SIZE)
pygame.draw.line(screen, color, (0, y), (width, y))
################################################################################
# 1. Drawing nodes and links
################################################################################
def draw_node(screen: pygame.Surface, node: _Node, pos: Tuple[int, int]) -> None:
"""Draw a node on the screen at the given position.
pos represents the coordinates of the *top-left* corner of the node.
Your drawing of the the node should include:
- A rectangle split vertically into two halves.
- The item stored in the node, displayed in the left half.
You may assume the string representation of item is at most 3 characters
long. You'll need to use the draw_text function we've provided above.
NOTE: Do not draw the arrow representing the link to the next node in this function.
You'll implement that part of the visualization separately in the draw_link function.
We strongly recommend initializing new constants at the top of this file to represent
node width, height, and colour.
"""
def draw_link(screen: pygame.Surface, start: Tuple[int, int], end: Tuple[int, int]) -> None:
"""Draw a line representing a link from `start` to `end`.
To indicate which end is the "start" of the line, draw a small circle at the starting point
(like the diagrams we've seen in class).
The rest of your link can be a simple line; you may, but are not required, to draw an
arrowhead at the end of the line.
"""
def draw_three_nodes(screen_size: Tuple[int, int]) -> None:
"""Draw three nodes on a pygame screen of the given size.
You may choose the coordinates for the nodes, as long as they do not overlap with each other
and are separated by at least 10 pixels. Each link must start in the CENTRE of the start node's
right half, and must end on the border of the end node (not inside the end node).
This matches the style of node from lecture.
The third node should link to "None", which you should visualize by calling draw_text.
"""
screen = initialize_screen(screen_size, [])
node1 = _Node(1)
node2 = _Node(2)
node3 = _Node(3)
node1.next = node2
node2.next = node3
# TODO: Complete this function so that it draws the above three nodes and the
# links between them and "None". Once you are done, remove this comment.
...
# Don't change the code below (it simply waits until you close the Pygame window)
pygame.display.flip()
pygame.event.wait()
pygame.display.quit()
################################################################################
# 2. Drawing a full linked list
################################################################################
def draw_list(screen: pygame.Surface, lst: LinkedList, show_grid: bool = False) -> None:
"""Draw the given linked list on the screen.
The linked list nodes should be drawn in a grid pattern with GRID_SIZE rows and columns.
See the assignment handout for details and constraints on your drawing.
If the show_grid parameter is True, the grid is drawn on the board. You can use
the grid to help you make sure you're drawing nodes in the correct locations.
By default, show_grid is False.
Preconditions:
- len(lst) < GRID_SIZE * GRID_SIZE
As with draw_node, we strongly recommend initializing new constants at the top of this file
to store numbers used to position the nodes.
We have started the linked list traversal pattern for you. Note that we're accessing
a private LinkedList attribute _first, which is generally not a good practice but we're
allowing you to do so here to simplify your code a little.
"""
if show_grid:
draw_grid(screen)
curr = lst._first
curr_index = 0
while curr is not None:
...
curr = curr.next
curr_index = curr_index + 1
################################################################################
# 3. Handling user events
################################################################################
def run_visualization(screen_size: tuple[int, int], ll_class: type,
show_grid: bool = False) -> None:
"""Run the linked list visualization.
Initialize a screen of the given size, and show the grid when show_grid is True.
ll_class is the type of linked list to use.
Preconditions:
- ll_class is LinkedList or issubclass(ll_class, LinkedList)
This function is provided for you for Part 3, and you *should not change it*.
Instead, your task is to implement the helper function handle_mouse_click (and
any other helpers you decide to add).
"""
# Initialize the Pygame screen, allowing for mouse click events.
screen = initialize_screen(screen_size, [pygame.MOUSEBUTTONDOWN])
# Initialize a random linked list of length 50.
lst = ll_class(random.sample(range(-99, 1000), 50))
while True:
# Draw the list (on a white background)
screen.fill(THECOLORS['white'])
draw_list(screen, lst, show_grid)
pygame.display.flip()
# Wait for an event (either pygame.MOUSEBUTTONDOWN or pygame.QUIT)
event = pygame.event.wait()
if event.type == pygame.MOUSEBUTTONDOWN:
# Call our event handling method
handle_mouse_click(lst, event, screen.get_size())
elif event.type == pygame.QUIT:
break
pygame.display.quit()
def handle_mouse_click(lst: LinkedList, event: pygame.event.Event,
screen_size: Tuple[int, int]) -> None:
"""Handle a mouse click event.
A pygame mouse click event object has two attributes that are important for this method:
- event.pos: the (x, y) coordinates of the mouse click
- event.button: an int representing which mouse button was clicked.
1: left-click, 3: right-click
The screen_size is a tuple of (width, height), and should be used together with
event.pos to determine which cell is being clicked. If a click happens exactly on
the boundary between two cells, you may decide which cell is selected.
Preconditions:
- event.type == pygame.MOUSEBUTTONDOWN
- screen_size[0] >= 200
- screen_size[1] >= 200
"""
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'max-line-length': 100,
'disable': ['E1136'],
'exclude-protected': ['_first'],
'extra-imports': ['random', 'pygame', 'pygame.colordict', 'a1_linked_list'],
'generated-members': ['pygame.*']
})
import python_ta.contracts
python_ta.contracts.check_all_contracts()