lambda:
Anonymous functions are simplified forms of function definitions.
- they do not have a name.
- the parameter list is not in parentheses.
- the function body is just a single expression, which is implicitly returned.
This simplicity is convenient especially when the function is simply passed as an argument, as in the last example above.
Example:
data = [1, 3, 6, 9, 18]
# square elements in a list by lambda
data3 = map(lambda x: x ** 2, data)
# filter the even number
data4 = filter(lambda val: val % 2 == 0, data)
Tree
A rooted tree is a hierarchical data structure with applications in many areas of computer science. These applications include searching, sorting, modeling biological processes (such as genealogy), and representing hierarchical programming constructs such as arithmetic expressions.
This distinction between leaf nodes and internal nodes allows us to specify simple recursive definitions for several important structural quantities associated with trees.
- The number of nodes in a tree satisfies:
If the root node of the tree is a leaf:
return 1
else:
return 1 + the sum of the number of nodes for each subtree
- The number of leaves in a tree satisfies:
If the root node of the tree is a leaf:
return 1
else:
return the sum of the number of leaves for each subtree
- The height of a tree is the length of the longest sequence of edges from the root to a leaf. The height can be computed recursively via:
If the root node of the tree is a leaf:
return 0
else:
return 1 + the maximum of the heights of the subtrees
In the case of binary trees, the two subtrees of a node are usually referred to as the left subtree and the right subtree. For binary trees, there are three common types of tree traversals:
- Pre-order traversals - process root, process left subtree, process right subtree
- Post-order traversals - process left subtree, process right subtree, process root
- In-order traversals - process left subtree, process root, process right subtree
ArithmeticExpression class implement uses In-order traversals
Mini Project – TicTacToe with Minimax
- Use Minimax to return the best move
- Use Memo_dict to record the score the boards, reduce iteration
- Use equivalent_boards by mirror & rotation to optimize
- Use opt_empty_cell to reduce the empty cell search list
"""
Mini-max Tic-Tac-Toe Player
"""
import poc_ttt_gui
import poc_ttt_provided as provided
# Set timeout, as mini-max can take a long time
import codeskulptor
codeskulptor.set_timeout(600)
import math
import random
# SCORING VALUES - DO NOT MODIFY
SCORES = {provided.PLAYERX: 1,
provided.DRAW: 0,
provided.PLAYERO: -1}
# helper function
def transpose_board(board):
"""
return the transposed board
"""
dim = board.get_dim()
tboard = provided.TTTBoard(dim)
for row in range(dim):
for col in range(dim):
if board.square(row, col) != provided.EMPTY:
tboard.move(col, row, board.square(row, col))
return tboard
def x_mirror_board(board):
"""
return the board mirrored by x
"""
dim = board.get_dim()
xboard = provided.TTTBoard(dim)
for row in range(dim):
for col in range(dim):
if board.square(row, col) != provided.EMPTY:
xrow = dim - row - 1
xboard.move(xrow, col, board.square(row, col))
return xboard
def y_mirror_board(board):
"""
return the board mirrored by y
"""
dim = board.get_dim()
yboard = provided.TTTBoard(dim)
for row in range(dim):
for col in range(dim):
if board.square(row, col) != provided.EMPTY:
ycol = dim - col - 1
yboard.move(row, ycol, board.square(row, col))
return yboard
def rotate_board(board, retation = 90):
"""
retate the board by 0, 90, 180, 270 degree
"""
dim = board.get_dim()
rboard = provided.TTTBoard(dim)
valid_rotation_list = [0, 90, 180, 270]
if retation not in valid_rotation_list:
return "Invalid retation", retation
for row in range(dim):
for col in range(dim):
if retation == 90:
rboard.move(dim - col - 1, row, board.square(row, col))
elif retation == 180:
rboard.move(dim - row - 1, dim - col - 1, board.square(row, col))
elif retation == 270:
rboard.move(col, dim - row - 1, board.square(row, col))
else:
rboard.move(row, col, board.square(row, col))
return rboard
def all_equivalent_board(board):
"""
return all the equivalent boards
"""
all_boards = []
rotation_list = [0, 90, 180, 270]
for rotation in rotation_list:
rboard = rotate_board(board, rotation)
tboard = transpose_board(rboard)
all_boards.append(rboard)
all_boards.append(tboard)
return all_boards
def opt_empty_cell(board):
"""
reduce the empty cell according to the board
"""
dim = board.get_dim()
all_empty_cells = board.get_empty_squares()
xboard = x_mirror_board(board)
if str(board) == str(xboard):
xrow = int(math.ceil(dim / 2.0))
for row in range(xrow, dim):
for col in range(dim):
cell = (row, col)
if cell in all_empty_cells:
all_empty_cells.remove(cell)
yboard = y_mirror_board(board)
if str(board) == str(yboard):
ycol = int(math.ceil(dim / 2.0))
for row in range(dim):
for col in range(ycol, dim):
cell = (row, col)
if cell in all_empty_cells:
all_empty_cells.remove(cell)
tboard = transpose_board(board)
if str(board) == str(tboard):
xrow = int(math.floor(dim / 2.0))
ycol = int(math.ceil(dim / 2.0))
for row in range(xrow, dim):
for col in range(ycol):
if row != col:
cell = (row, col)
if cell in all_empty_cells:
all_empty_cells.remove(cell)
return all_empty_cells
def mm_move_helper(board, player, memo_dict):
"""
helper function for mm_move for not corrupt origial structure
"""
if (str(board)) in memo_dict.keys():
return memo_dict[(str(board))]
# DFS the tree until game finishes
if board.check_win() == None:
cell_dict = {}
# score the next moves
# for cell in board.get_empty_squares():
for cell in opt_empty_cell(board):
board_clone = board.clone()
board_clone.move(cell[0], cell[1], player)
move = mm_move_helper(board_clone, provided.switch_player(player), memo_dict)
# stop DFS search as long as the best solution is found
if move[0] * SCORES[player] == 1:
# memo the result
all_boards = all_equivalent_board(board)
for a_board in all_boards:
memo_dict[str(a_board)] = (move[0], cell)
return move[0], cell
if move[0] * SCORES[player] not in cell_dict.keys():
cell_dict[move[0] * SCORES[player]] = cell
# return the best score and move
max_score = max(cell_dict.keys())
best_score = max_score * SCORES[player]
best_move = cell_dict[max_score]
else:
# Score the board when game finishes
best_score = SCORES[board.check_win()]
best_move = (-1, -1)
# memo the result
all_boards = all_equivalent_board(board)
for a_board in all_boards:
memo_dict[str(a_board)] = (best_score, best_move)
return best_score, best_move
def mm_move(board, player):
"""
Make a move on the board.
Returns a tuple with two elements. The first element is the score
of the given board and the second element is the desired move as a
tuple, (row, col).
memo_dict is a dictionary that stores previously computed results
"""
return mm_move_helper(board, player, {})
def move_wrapper(board, player, trials):
"""
Wrapper to allow the use of the same infrastructure that was used
for Monte Carlo Tic-Tac-Toe.
"""
move = mm_move(board, player)
assert move[1] != (-1, -1), "returned illegal move (-1, -1)"
return move[1]
# Test game with the console or the GUI.
# Uncomment whichever you prefer.
# Both should be commented out when you submit for
# testing to save time.
#provided.play_game(move_wrapper, 1, False)
poc_ttt_gui.run_gui(3, provided.PLAYERX, move_wrapper, 1, False)