Merge branch 'main' of github.com:hykilpikonna/CSC111 into main
This commit is contained in:
@@ -89,7 +89,7 @@ class GameTree:
|
||||
return None
|
||||
|
||||
def add_subtree(self, subtree: GameTree) -> None:
|
||||
"""Add a subtree to this game tree."""
|
||||
"""Add a subtree to this game tree and updates white win probability."""
|
||||
self._subtrees.append(subtree)
|
||||
self._update_white_win_probability()
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ def part1_runner(games_file: str, n: int, black_random: bool) -> None:
|
||||
"""
|
||||
tree = load_game_tree(games_file)
|
||||
white = RandomTreePlayer(tree)
|
||||
black = a2_minichess.RandomPlayer() if black_random else white
|
||||
black = a2_minichess.RandomPlayer() if black_random else RandomTreePlayer(tree)
|
||||
a2_minichess.run_games(n, white, black, show_stats=True)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
"""CSC111 Winter 2022 Prep 8: Programming Exercises
|
||||
|
||||
Instructions (READ THIS FIRST!)
|
||||
===============================
|
||||
|
||||
This module contains the graph implementation we studied in lecture, with a few
|
||||
additional methods for you to implement on this exercise.
|
||||
|
||||
We have marked each place you need to write code with the word "TODO".
|
||||
As you complete your work in this file, delete each TODO comment.
|
||||
|
||||
You may add additional doctests, but they will not be graded. You should test your work
|
||||
carefully before submitting it!
|
||||
|
||||
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) 2022 Mario Badr and David Liu.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Graph:
|
||||
"""A graph.
|
||||
"""
|
||||
# Private Instance Attributes:
|
||||
# - _vertices:
|
||||
# A collection of the vertices contained in this graph.
|
||||
# Maps item to _Vertex object.
|
||||
_vertices: dict[Any, _Vertex]
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize an empty graph (no vertices or edges)."""
|
||||
self._vertices = {}
|
||||
|
||||
def add_vertex(self, item: Any) -> None:
|
||||
"""Add a vertex with the given item to this graph.
|
||||
|
||||
The new vertex is not adjacent to any other vertices.
|
||||
"""
|
||||
self._vertices[item] = _Vertex(item, set())
|
||||
|
||||
def add_edge(self, item1: Any, item2: Any) -> None:
|
||||
"""Add an edge between the two vertices with the given items in this graph.
|
||||
|
||||
Raise a ValueError if item1 or item2 do not appear as vertices in this graph.
|
||||
|
||||
Preconditions:
|
||||
- item1 != item2
|
||||
"""
|
||||
if item1 in self._vertices and item2 in self._vertices:
|
||||
v1 = self._vertices[item1]
|
||||
v2 = self._vertices[item2]
|
||||
|
||||
# Add the new edge
|
||||
v1.neighbours.add(v2)
|
||||
v2.neighbours.add(v1)
|
||||
else:
|
||||
# We didn't find an existing vertex for both items.
|
||||
raise ValueError
|
||||
|
||||
def connected(self, item1: Any, item2: Any) -> bool:
|
||||
"""Return whether item1 and item2 are connected vertices in this graph.
|
||||
|
||||
Return False if item1 or item2 do not appear as vertices in this graph.
|
||||
|
||||
>>> g = Graph()
|
||||
>>> g.add_vertex(1)
|
||||
>>> g.add_vertex(2)
|
||||
>>> g.add_vertex(3)
|
||||
>>> g.add_vertex(4)
|
||||
>>> g.add_edge(1, 2)
|
||||
>>> g.add_edge(2, 3)
|
||||
>>> g.connected(1, 3)
|
||||
True
|
||||
>>> g.connected(1, 4)
|
||||
False
|
||||
"""
|
||||
if item1 in self._vertices and item2 in self._vertices:
|
||||
v1 = self._vertices[item1]
|
||||
return v1.check_connected(item2, set()) # Pass in an empty "visited" set
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_connected_component(self, item: Any) -> set:
|
||||
"""Return a set of all ITEMS connected to the given item in this graph.
|
||||
|
||||
Raise a ValueError if item does not appears as a vertex in this graph.
|
||||
|
||||
>>> g = Graph()
|
||||
>>> for i in range(0, 5):
|
||||
... g.add_vertex(i)
|
||||
>>> g.add_edge(0, 1)
|
||||
>>> g.add_edge(1, 2)
|
||||
>>> g.add_edge(1, 3)
|
||||
>>> g.add_edge(2, 3)
|
||||
>>> g.get_connected_component(0) == {0, 1, 2, 3}
|
||||
True
|
||||
|
||||
Note: we've implemented this method for you, and you should not change it.
|
||||
Instead, your task is to implement _Vertex.get_connected_component below.
|
||||
"""
|
||||
if item not in self._vertices:
|
||||
raise ValueError
|
||||
else:
|
||||
return self._vertices[item].get_connected_component(set())
|
||||
|
||||
def in_cycle(self, item: Any) -> bool:
|
||||
"""Return whether the given item is in a cycle in this graph.
|
||||
|
||||
Return False if item does not appears as a vertex in this graph.
|
||||
|
||||
KEY OBSERVATION. A vertex v is in a cycle if and only if:
|
||||
v has two distinct neighbours u and w that are connected to each other
|
||||
by a path that doesn't use v.
|
||||
|
||||
>>> g = Graph()
|
||||
>>> for i in range(0, 5):
|
||||
... g.add_vertex(i)
|
||||
>>> g.add_edge(0, 1)
|
||||
>>> g.add_edge(1, 2)
|
||||
>>> g.add_edge(1, 3)
|
||||
>>> g.add_edge(2, 3)
|
||||
>>> g.in_cycle(1)
|
||||
True
|
||||
>>> g.in_cycle(0)
|
||||
False
|
||||
>>> g.add_edge(4, 0)
|
||||
>>> g.in_cycle(0)
|
||||
False
|
||||
|
||||
Implementation notes:
|
||||
1. This method should call _Vertex.check_connected (following the above
|
||||
description).
|
||||
2. Don't try to make this method recursive, or copy and paste the implementation
|
||||
of _Vertex.check_connected! That's not necessary here.
|
||||
"""
|
||||
# Does not exist
|
||||
if item not in self._vertices:
|
||||
return False
|
||||
v = self._vertices[item]
|
||||
|
||||
# Combinations
|
||||
for u in v.neighbours:
|
||||
for w in v.neighbours:
|
||||
# Distinct combinations
|
||||
if u == w or u == v or w == v:
|
||||
continue
|
||||
|
||||
if u.check_connected(w.item, {v}):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class _Vertex:
|
||||
"""A vertex in a graph.
|
||||
|
||||
Instance Attributes:
|
||||
- item: The data stored in this vertex.
|
||||
- neighbours: The vertices that are adjacent to this vertex.
|
||||
|
||||
Representation Invariants:
|
||||
- self not in self.neighbours
|
||||
- all(self in u.neighbours for u in self.neighbours)
|
||||
"""
|
||||
item: Any
|
||||
neighbours: set[_Vertex]
|
||||
|
||||
def __init__(self, item: Any, neighbours: set[_Vertex]) -> None:
|
||||
"""Initialize a new vertex with the given item and neighbours."""
|
||||
self.item = item
|
||||
self.neighbours = neighbours
|
||||
|
||||
def check_connected(self, target_item: Any, visited: set[_Vertex]) -> bool:
|
||||
"""Return whether this vertex is connected to a vertex corresponding to the target_item,
|
||||
WITHOUT using any of the vertices in visited.
|
||||
|
||||
Preconditions:
|
||||
- self not in visited
|
||||
"""
|
||||
if self.item == target_item:
|
||||
# Our base case: the target_item is the current vertex
|
||||
return True
|
||||
else:
|
||||
visited.add(self) # Add self to the set of visited vertices
|
||||
for u in self.neighbours:
|
||||
if u not in visited: # Only recurse on vertices that haven't been visited
|
||||
if u.check_connected(target_item, visited):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_connected_component(self, visited: set[_Vertex]) -> set:
|
||||
"""Return a set of all ITEMS connected to self by a path that does not use
|
||||
any vertices in visited.
|
||||
|
||||
The items of the vertices in visited CANNOT appear in the returned set.
|
||||
|
||||
Preconditions:
|
||||
- self not in visited
|
||||
|
||||
Implementation notes:
|
||||
1. This can be implemented in a similar way to _Vertex.check_connected.
|
||||
2. This method must be recursive, and will have an implicit base case:
|
||||
when all vertices in self.neighbours are already in visited.
|
||||
3. Use a loop accumulator to store a set of the vertices connected to self.
|
||||
|
||||
>>> g = Graph()
|
||||
>>> for i in range(0, 7):
|
||||
... g.add_vertex(i)
|
||||
>>> g.add_edge(0, 1)
|
||||
>>> g.add_edge(1, 2)
|
||||
>>> g.add_edge(1, 3)
|
||||
>>> g.add_edge(2, 3)
|
||||
>>> g.get_connected_component(1) == {0, 1, 2, 3}
|
||||
True
|
||||
>>> g.add_edge(4, 0)
|
||||
>>> g.get_connected_component(0) == {0, 1, 2, 3, 4}
|
||||
True
|
||||
>>> g.get_connected_component(5)
|
||||
{5}
|
||||
>>> g._vertices[5].get_connected_component({g._vertices[5]})
|
||||
set()
|
||||
>>> g._vertices[6].get_connected_component(set())
|
||||
{6}
|
||||
"""
|
||||
if self in visited:
|
||||
return set()
|
||||
visited.add(self)
|
||||
nums = {self.item}
|
||||
for u in self.neighbours:
|
||||
nums = nums.union(u.get_connected_component(visited))
|
||||
return nums
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Note: we are NOT using python_ta.contracts for this prep.
|
||||
# (Feel free to ask why in office hours/Campuswire.)
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
import python_ta
|
||||
python_ta.check_all(config={
|
||||
'max-line-length': 100,
|
||||
'disable': ['E1136'],
|
||||
'max-nested-blocks': 4
|
||||
})
|
||||
Reference in New Issue
Block a user