Files
CSC111/assignments/A2/a2_part2.py
T
wuliaozhiji ebe8170daa [+] A2 P2.1
2022-02-19 14:31:58 -05:00

145 lines
5.4 KiB
Python

"""CSC111 Winter 2021 Assignment 2: Trees, Chess, and Artificial Intelligence (Part 2)
Instructions (READ THIS FIRST!)
===============================
This Python module contains the start of functions and/or classes you'll define
for Part 2 of this assignment. Please note that in addition to this file, you will
also need to modify a2_game_tree.py by following the instructions on the assignment
handout. You should NOT make any changes to a2_minichess.py.
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, David Liu, and Isaac Waller.
"""
import random
from typing import Optional
import a2_game_tree
import a2_minichess
def generate_complete_game_tree(root_move: str, game_state: a2_minichess.MinichessGame,
d: int) -> a2_game_tree.GameTree:
"""Generate a complete game tree of depth d for all valid moves from the current game_state.
For the returned GameTree:
- Its root move is root_move.
- Its `is_white_move` attribute is set using the current game_state.
- It contains all possible move sequences of length <= d from game_state.
For each node in the tree, its subtrees appear in the same order that their
moves were returned by game_state.get_valid_moves(),
- If d == 0, a size-one GameTree is returned.
Note that some paths down the tree may have length < d, because they result in an end state
(win or draw) from game_state in fewer than d moves.
Preconditions:
- d >= 0
- root_move == GAME_START_MOVE or root_move is a valid chess move
- if root_move == GAME_START_MOVE, then game_state is in the initial game state
Implementation hints:
- This function must be implemented recursively.
- In the recursive step, use the MinichessGame.copy_and_make_move method to create
a copy of the game state with one new move made.
- You'll need to review the public interface of the MinichessGame class to see what
methods are available to help implement this function.
WARNING: we recommend not calling this function with depth greater than 6, as this will
likely take a very long time on your computer.
>>> g = a2_minichess.MinichessGame()
>>> str(generate_complete_game_tree('*', g, 0)).strip()
"* -> White's move"
>>> t = generate_complete_game_tree('*', g, 2)
>>> {sub.move for sub in t.get_subtrees()} == {'a2b3', 'b2c3', 'b2a3', 'c2d3', 'c2b3', 'd2c3'}
True
>>> t: a2_game_tree.GameTree = t.find_subtree_by_move('c2d3')
>>> len(t.get_subtrees())
1
>>> t.get_subtrees()[0].move
'd4d3'
"""
tree = a2_game_tree.GameTree(root_move, game_state.is_white_move())
# Base case
if d == 0:
return tree
# Add all valid moves
for move in game_state.get_valid_moves():
# Recursive step
next_tree = generate_complete_game_tree(move, game_state.copy_and_make_move(move), d - 1)
tree.add_subtree(next_tree)
return tree
class GreedyTreePlayer(a2_minichess.Player):
"""A Minichess player that plays greedily based on a given GameTree.
See assignment handout for description of its strategy.
"""
# Private Instance Attributes:
# - _game_tree:
# The GameTree that this player uses to make its moves. If None, then this
# player just makes random moves.
_game_tree: Optional[a2_game_tree.GameTree]
def __init__(self, game_tree: a2_game_tree.GameTree) -> None:
"""Initialize this player.
Preconditions:
- game_tree represents a game tree at the initial state (root is '*')
"""
self._game_tree = game_tree
def make_move(self, game: a2_minichess.MinichessGame, previous_move: Optional[str]) -> str:
"""Make a move given the current game.
previous_move is the opponent player's most recent move, or None if no moves
have been made.
Preconditions:
- There is at least one valid move for the given game
"""
def part2_runner(d: int, n: int, white_greedy: bool) -> None:
"""Create a complete game tree with the given depth, and run n games where
one player is a GreedyTreePlayer and the other is a RandomPlayer.
The GreedyTreePlayer uses the complete game tree with the given depth.
If white_greedy is True, the White player is the GreedyTreePlayer and Black is a RandomPlayer.
This is switched when white_greedy is False.
Precondtions:
- d >= 0
- n >= 1
Implementation notes:
- Your implementation MUST correctly call a2_minichess.run_games. You may choose
the values for the optional arguments passed to the function.
"""
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'max-line-length': 100,
'max-nested-blocks': 4,
'disable': ['E1136'],
'extra-imports': ['random', 'a2_minichess', 'a2_game_tree']
})
# Sample call to part2_runner (you can change this, just keep it in the main block!)
# part2_runner(5, 50, False)